과거 해커톤 때 팀원들 교육용으로 만들었던 투표 API를 코프링으로 옮겨보려한다.
https://github.com/Aiden-Kwak/NextJsDemo
GitHub - Aiden-Kwak/NextJsDemo: 팀원 교육용으로 제작
팀원 교육용으로 제작. Contribute to Aiden-Kwak/NextJsDemo development by creating an account on GitHub.
github.com
원하는 응답
GET /api/get-vote
{"id":1,"bidenCount":0,"trumpCount":0}
POST /api/post-vote with {"vote": "Biden"}
{"id":1,"bidenCount":1,"trumpCount":0}
POST /api/post-vote with {"vote": "Trump"}
{"id":1,"bidenCount":1,"trumpCount":1}
1. 구조

대충 이렇다. 더 그럴듯한 구조를 발견하지 못하면 당분간 이렇게 작성할 듯하다.
controller, dto, model, repository, service 구조를 가져가려한다.
- controller:
클라이언트 요청을 제일 먼저 받는 곳. 요쳥을 service에 전달하고 응답을 반환한다.
- service:
비즈니스 로직을 여기 모아둔다. 여기선 투표수 증가처리. controller가 요청한 작업을 여기서 수행. DRF로 따지면 view에 가까움
- repository:
JPA써서 데이터베이스와 직접 통신. Vote 데이터를 조회,저장,삭제
* JPA(Java Persistence API)는 자바 진영에서 ORM 기술표준으로 쓰는 인터페이스 모아둔거
- model
- dto
2. model/Vote.kt
package com.example.vote.model
// 얘가 JPA에서 제공하는 클래스랑 어노테이션 가져옴. @Entity, @Id, @GeneratedValue등
import jakarta.persistence.*
@Entity // 이 클래스가 JPA 엔티티임을 표시
data class Vote(
@Id // id를 pk로 지정. DRF랑 다르게 직접 지정필요한듯
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
var bidenCount: Int = 0,
var trumpCount: Int = 0
)
@GeneratedValue(strategy = GenerationType.IDENTITY) 이거는 JPA가 엔티티의 PK 생성방식을 지정함. GenerationType.IDENTITY에 의해서 데이터 삽입시 디비가 자동으로 고유값을 할당한다.
3. controller/VoteController.kt
package com.example.vote.controller
import com.example.vote.dto.VoteRequest
import com.example.vote.model.Vote
import com.example.vote.service.VoteService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api")
class VoteController(
private val voteService: VoteService
) {
@GetMapping("/get-vote")
fun getVote(): ResponseEntity<Vote> {
val vote = voteService.getVote()
return ResponseEntity.ok(vote)
}
@PostMapping("/post-vote")
fun postVote(@RequestBody request: VoteRequest): ResponseEntity<Any> {
return try {
val updatedVote = voteService.postVote(request)
ResponseEntity.status(HttpStatus.CREATED).body(updatedVote)
} catch (e: IllegalArgumentException) {
ResponseEntity.badRequest().body(mapOf("error" to e.message))
}
}
}
- 모든 엔드포인트를 /api로
- private val voteService: VoteService 통해서 VoteService 주입 받음
4. repository/VoteRepository.kt
package com.example.vote.repository
import com.example.vote.model.Vote
import org.springframework.data.jpa.repository.JpaRepository
interface VoteRepository : JpaRepository<Vote, Long>
JpaRepository를 상속받아 Vote 엔티티에 대해 Long 타입 ID 사용하는 기본 CRUD 메서드 사용할 수 있다.
save(), findById(), findAll(), delete() 같은거 사용 가능.
5. service/VoteService.kt
package com.example.vote.service
import com.example.vote.dto.VoteRequest
import com.example.vote.model.Vote
import com.example.vote.repository.VoteRepository
import org.springframework.stereotype.Service
@Service
class VoteService(
private val voteRepository: VoteRepository
) {
fun getVote(): Vote {
return voteRepository.findAll().firstOrNull() ?: voteRepository.save(Vote())
}
fun postVote(request: VoteRequest): Vote {
val vote = voteRepository.findAll().firstOrNull() ?: voteRepository.save(Vote())
when (request.vote) {
"Biden" -> vote.bidenCount += 1
"Trump" -> vote.trumpCount += 1
else -> throw IllegalArgumentException("Invalid vote option")
}
return voteRepository.save(vote)
}
}
> getVote:
- findAll() : 데이터베이스에서 모든 투표기록 가져와
- firstOrNull(): 하나라도 있으면 첫번째 투표기록 사용하고 없으면 널반환
- ?:voteRepository.save(Vote()) : 널이면 다 0인 Vote 객체 생성하고, 저장한후 반환
> postVote: 투표수 업데이트하고 업데이트 반환
'웹 프로그래밍 > Kotlin' 카테고리의 다른 글
Intellij에서 실행안되는 문제 (0) | 2025.04.18 |
---|---|
코틀린 시작 기록(2) (0) | 2025.04.07 |
코틀린 시작 기록 (1) (1) | 2025.04.06 |