실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기

Ch03. 요구사항 추가(type, 대출현황) - Java 서버를 Kotlin 서버로 리팩토링하자 - 책의 분야 추가하기

webmaster 2022. 11. 5. 22:28
728x90

추가적인 요구사항이 생겼다 -> 책을 등록할 때, '분야'를 선택해야 한다(분야에는 5가지 존재한다 - 컴퓨터/ 경제/ 사회/ 언어/ 과학)

Book 도메인에 type 필드를 추가

  • null이 불가능하므로, String 타입으로 선언하자.
  • 한번 분야가 정해진 후, 업데이트 요구사항이 없으므로, 불변(val)으로 선언하자

로직 변경

book

@Entity
class Book(
    val name: String,
    val type: String, //책 타입 추가
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,
) {
    //기본 생성자가 없는 오류가 발생(플러그인을 받으면 오류 해결)

    init { // 초기화 블록
        if (name.isBlank()) {
            throw IllegalArgumentException("이름은 비어 있을 수 없습니다")
        }
    }

}

BookRequest

data class BookRequest(
    val name: String,
    val type: String,
)

BookService

//Save 메서드만 변경
@Transactional
fun saveBook(request: BookRequest) {
    val book = Book(request.name, request.type)
    bookRepository.save(book)
}
  • Book 도메인에, type 필드를 추가하였다.
  • 영향범위는 Service와 DTO이다(도메인을 기준으로 상위 계층에 영향 범위가 전파) -> Service에서는 현재 save 로직에서만 추가해 주면 된다.
  • 문제점 : 테스트 코드에서 saveBookTest를 제외한 테스트는 사실 Book을 생성하는 부분에 큰 관심이 없는 테스트이지만, Book 도메인이 변경되면서, Book을 생성하는 부분에서 문제가 발생한다 
    • 이것이 문제가 되는 이유는 테스트 코드 역시 유지보수의 대상이 되는데, 프로젝트 규모가 클수록 도메인을 변경할 때마다, 테스트 코드의 수정이 많아지게 되는 문제가 발생한다.
    • 또한 type이 테스트 실패의 결과까지 영향을 줄 정도로 중요한 의미를 가지는 것은 아니기 떄문에 문제가 된다.
    • Book 객체를 만드는 함수를 미리 만들어 두어 이를 해결할 수도 있다.

Object Mother 패턴

Book

@Entity
class Book(
    val name: String,
    val type: String, //책 타입 추가
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,
) {
    //기본 생성자가 없는 오류가 발생(플러그인을 받으면 오류 해결)

    init { // 초기화 블록
        if (name.isBlank()) {
            throw IllegalArgumentException("이름은 비어 있을 수 없습니다")
        }
    }

    companion object{ //동반객체가 가장 아래 들어가는게 컨벤션
        //Object Mother 패턴
        fun fixture(
            name: String="책 이름",
            type: String = "COMPUTER",
            id:Long? = null,
        ): Book{
            return Book(
                name = name,
                type = type,
                id = id,
            )
        }
    }
}

Test

@Test
@DisplayName("책 등록이 정상 동작한다")
fun saveBookTest() {
    //given
    val request = BookRequest("이상한 나라의 엘리스", "COMPUTER")

    //when
    bookService.saveBook(request)

    //then
    val books = bookRepository.findAll()
    assertThat(books).hasSize(1)
    assertThat(books[0].name).isEqualTo("이상한 나라의 엘리스")
    assertThat(books[0].type).isEqualTo("COMPUTER")
}
  • 객체를 생성해주는 정적 메서드를 만든다 => 해당 파라미터에는 디폴트 파라미터가 쓰여 값을 넣어주어도 안 넣어 주어도 생성해 준다.
  • 동반 객체 같은 경우 코틀린에서 가장 아래에 사용하는 것이 컨벤션이다.
  • saveBookTest를 제외한 나머지 코드는 수정하지 않아도 된다.
728x90