실무 프로젝트로 배우는 Kotlin & Spring/이슈 관리 서비스 개발하기

이슈 등록

webmaster 2022. 11. 27. 18:21
728x90

BaseEntity

@MappedSuperclass
@EntityListeners(AuditingEntityListener::class)
abstract class BaseEntity(
    @CreatedDate
    var createdAt: LocalDateTime? = null,

    @LastModifiedDate
    var updatedAt: LocalDateTime? = null,

    ) { //추상 클래스
}
  • 공통 엔티티로 만들 BaseEntity 추가
  • 모든 엔티티에서 사용할 공통된 속성을 정의하고 싶은 경우 @MappedSuperclass를 사용해 부모 엔티티에서 공통 속성을 정의하고, 하위 엔티티에서 상속받아 사용할 수 있다.
  • @EntityListeners는 엔티티에 특정한 이벤트가 발생하면 정해진 콜백 처리를 할 수 있다.
  • AudidingEventListener를 사용하면, 엔티티가 생성될 때 @CreatedDate 가 붙은 프로퍼티에 생성일을 넣어주고, 엔티티 변경이 일어날 때, @LastModifiedDate가 붙은 프로퍼티에 변경 일시를 자동으로 넣어준다.
@SpringBootApplication
@EnableJpaAuditing
class FastcampusIssueServiceApplication

fun main(args: Array<String>) {
    runApplication<FastcampusIssueServiceApplication>(*args)
}
  • AudidingEventListener를 사용하기 위해서는 @EnableJpaAuditing 어노테이션이 필요하다

Issue

@Entity
@Table
class Issue(
    @Id @GeneratedValue(strategy = IDENTITY)
    val id: Long? = null,

    @Column
    var userId: Long,

    @Column
    var summary: String,

    @Column
    var description: String,

    @Column
    @Enumerated(STRING)
    var type: IssueType,

    @Column
    @Enumerated(STRING)
    var priority: IssuePriority,

    @Column
    @Enumerated(STRING)
    var status: IssueStatus,

    ) : BaseEntity() {

}

IssueType, IssuePriority, IssueStatus

enum class IssueType {
    BUG, TASK;

    companion object {
        //fun of(type: String) = valueOf(type.uppercase())
        operator fun invoke(type: String) = valueOf(type.uppercase())
    }
}
enum class IssueStatus {
    TODO, IN_PROGRESS, RESOLVED;

    companion object {
        operator fun invoke(status: String) = valueOf(status.uppercase())
    }
}
enum class IssuePriority {
    LOW, MEDIUM, HIGH;
    companion object{
        operator fun invoke(priority: String) = valueOf(priority.uppercase())
    }
}
  • of 메서드로 생성할 수도 있지만, 코틀린에서 제공하는 operator 키워드를 사용해 마치 생성자를 호출하듯 함수명 없이 호출이 가능하다.

Repository

interface IssueRepository : JpaRepository<Issue, Long> {
}

Servicer

@Service
class IssueService(
    private val issueRepository: IssueRepository,
) {
    @Transactional
    fun create(userId: Long, request: IssueRequest): IssueResponse {
        val issue = Issue(
            summary = request.summary,
            description = request.description,
            userId = userId,
            type = request.type,
            priority = request.priority,
            status = request.status
        )
        return IssueResponse(issueRepository.save(issue))
    }
}

IssueRequest, IssueResponse

data class IssueRequest(
    val summary: String,
    val description: String,
    val type: IssueType,
    val priority: IssuePriority,
    val status: IssueStatus,
)

data class IssueResponse(
    val id: Long,
    val summary: String,
    val description: String,
    val userId: Long,
    val type: IssueType,
    val priority: IssuePriority,
    val status: IssueStatus,

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    val createdAt: LocalDateTime?,

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    val updatedAt: LocalDateTime?,
) {
    companion object {
        operator fun invoke(issue: Issue) =
            with(issue) {
                IssueResponse(
                    id = id!!,
                    summary = summary,
                    description = description,
                    userId = userId,
                    type = type,
                    priority = priority,
                    status = status,
                    createdAt = createdAt,
                    updatedAt = updatedAt
                )
            }
    }
}
  • 생성자를 호출하는 것처럼 생성할 수 있도록 invoke를 정의하였다.
    • with안에서는 자기 자신을 가리키는 this 키워드를 생략할 수 있다.
  • @JsonFormat을 통해 생성일, 수정일을 json 포멧을 변경할 수 있다.

Controller

@RestController
@RequestMapping("/api/v1/issues")
class IssueController(
    private val issueService: IssueService,
) {
    @PostMapping
    fun create(
        authUser: AuthUser, //HandlerMethodArgumentResolver를 통해 값을 주입받는다.
        @RequestBody request: IssueRequest,
    ) = issueService.create(authUser.userId, request)
}
  • 이 전에 ArgumentResolver를 통해, authUser를 등록해 두었기 때문에 파라미터로 AuthUser만 사용해도 자동으로 주입해 준다.

 

728x90