분류 전체보기 1341

이슈 등록

BaseEntity @MappedSuperclass @EntityListeners(AuditingEntityListener::class) abstract class BaseEntity( @CreatedDate var createdAt: LocalDateTime? = null, @LastModifiedDate var updatedAt: LocalDateTime? = null, ) { //추상 클래스 } 공통 엔티티로 만들 BaseEntity 추가 모든 엔티티에서 사용할 공통된 속성을 정의하고 싶은 경우 @MappedSuperclass를 사용해 부모 엔티티에서 공통 속성을 정의하고, 하위 엔티티에서 상속받아 사용할 수 있다. @EntityListeners는 엔티티에 특정한 이벤트가 발생하면 정해진 콜백 처리..

공통 에러 처리

API를 개발할 땐 에러가 발생했을 때의 상황을 고려해 항상 일관된 에러 처리 규칙을 정해야 한다 클라이언트에선 일관된 에러 응답을 통해 정해진 동작을 정의할 수 있다 예를 들어 클라이언트에서 에러 응답의 code를 읽어서 사전에 정의된 에러 메시지를 보여 주거나 에러 페이지로 리다이렉트 시키는 등의 작업을 할 수 있다 에러 응답 모델 정의 data class ErrorResponse( val code: Int, // 어떤 오류가 발생했는지 알 수 있는 코드, 커스텀한 코드를 통해 어떤 오류가 발생한지 파악 val message: String, ) { } code는 어떤 오류가 발생했는지 파악할 수 있다 Http status code 만으로는 모든 에러에 대한 케이스를 처리하기 힘들며, 커스텀한 code..

API 스펙 정의

이슈 목록 조회 API GET {host}/api/v1/issues?status=TODO status: TODO(기본값), IN_PROGRESS, RESOLVED 응답 [ { "id":1, "comments":[ { "id":1, "issueId":1, "userId":1, "body":"이것은 댓글입니다", "username":"sanghoon" } ], "summary":"테스트", "description":"설명", "userId":1, "type":"TASK", "priority":"LOW", "status":"TODO", "createdAt":"2022-06-12 00:16:42", "updatedAt":"2022-06-12 00:16:42" } ] type : BUG , TASK priorit..

요구 사항 & 프로젝트 구성

요구 사항 이슈 관리 서비스 메인 상태에 따라 할 일, 진행 중, 완료 세 가지 탭 존재 기본 상태는 할일이고, 탭을 눌렀을 때 상태별 이슈 목록 API를 조회한다 이슈 등록 이슈를 생성하는데 필요한 데이터를 입력받아 이슈 등록 API를 호출한다. 이슈 상세 조회 이슈 상세를 눌렀을 때 선택된 이슈 상세 조회 API 호출한다. 저장 버튼이 눌렀을 때 변경된 데이터로 이슈 수정 API를 호출한다. 삭제 버튼을 누르면 이슈 삭제 API를 호출한다. 댓글 입력 후 엔터를 누르면 코멘트 등록 API를 호출한다. 댓글 수정 버튼을 누르면 댓글을 수정할 수 있고, 엔터를 누르면 코멘트 수정 API를 호출한다. 댓글 삭제 버튼을 누르면 코멘트 삭제 API를 호출한다 프로젝트 구성하기 Main 프로젝트 springwe..

Ch04. 요구사항 추가(책 통계, QueryDSL) - UserLoanHistoryRepository 리펙토링

UserLoanHistoryRepository interface UserLoanHistoryRepository : JpaRepository { fun findByBookName(bookName: String): UserLoanHistory? fun findByBookNameAndStatus(bookName: String, status: UserLoanStatus): UserLoanHistory? fun findByStatus(status: UserLoanStatus): List fun countByStatus(status: UserLoanStatus): Long } 해당 메서드들은 @Query를 사용한 것이 아닌 SpringDataJpa를 사용해서 만든 것이다 이는 리펙토링 해야 하나? -> 해야 된다(..

Ch04. 요구사항 추가(책 통계, QueryDSL) - QueryDSL 사용하기

UserRepositoryCustomImpl 작성하기(방식 1) Config 파일 @Configuration class QuerydslConfig( private val em: EntityManager ) { @Bean fun querydsl(): JPAQueryFactory{ return JPAQueryFactory(em) } } JPAQueryFactory는 entityManager를 파라미터로 받아 사용한다. 해당 빈을 등록해두어 QueryDsl을 사용하는 곳에서 주입받아서 사용하면 된다. UserRepositroy interface UserRepository : JpaRepository, UserRepositoryCustom{ fun findByName(name: String): User? } 더..

Ch04. 요구사항 추가(책 통계, QueryDSL) - QueryDSL 도입하기

기술적인 요구사항 현재 사용하는 JPQL은 몇 가지 단점이 있다. QueryDSL을 사용해 단점을 극복하자 @Query을 단점 문자열로 쿼리를 작성하기 때문에 버그를 찾기 어렵다. JPQL문법이 일반 SQL과 조금 달라 복잡한 쿼리를 작성할 때마다 찾아봐야 한다. 조건이 복잡한 동적쿼리를 작성할 때 함수가 계속 늘어난다.(동적 쿼리 작성이 어렵다) 프로덕션 코드 변경(도메인 코드 변경)에 취약하다 함수 이름 구성에 제약이 있다 QueryDSL은 코드로 쿼리를 작성하게 해주는 도구로 개발자가 편리하게 코드를 작성하도록 도와준다. QueryDSL 설정 build.gradle plugins { //.... id 'org.jetbrains.kotlin.plugin.spring' version '1.6.21' i..

Ch04. 요구사항 추가(책 통계, QueryDSL) - 애플리케이션 대신 DB로 기능 구현

다양한 SQL 그룹 함수 sum : 주어진 column의 합계를 계산한다. avg : 주어진 column의 평균을 계산한다. count : 개수를 센다 group by : 주어진 column을 기준으로 grouping 한다. -> 함수형 프로그래밍의 groupby와 유사하다. order by : 주어진 column을 기준으로 정렬한다. -> 내림차순, 오름차순 지정할 수 있다. 애플리케이션 대신 DB로 기능 구현하기 UserLoanHistoryRepository interface UserLoanHistoryRepository : JpaRepository { //... fun countByStatus(status: UserLoanStatus): Long } BookService @Transactional(..

Ch04. 요구사항 추가(책 통계, QueryDSL) - 책 통계 테스트 코드와 리펙토링

테스트 코드 책 대여 권수를 확인하는 테스트 @Test @DisplayName("책 대여 권수를 정상 확인한다") fun countLoanedBookTest() { //given val saveUser = userRepository.save(User("김준호", null)) userLoanHistoryRepository.saveAll( listOf( UserLoanHistory.fixture(saveUser, "A"), UserLoanHistory.fixture(saveUser, "B", UserLoanStatus.RETURNED), UserLoanHistory.fixture(saveUser, "C", UserLoanStatus.RETURNED), ) ) //when val result = bookSer..

Ch04. 요구사항 추가(책 통계, QueryDSL) - 책 통계 요구사항 추가

현재 대여 중인 책이 몇 권인지 보여준다. 분야별로 도서관에 등록되어 있는 책이 각각 몇 권인지 보여준다. count가 0이면, 반환 리스트에 존재하지 않아도 된다 현재 대여 중인 책의 권수 API Controller @GetMapping("/book/loan") fun countLoanedBook(): Int{ return bookService.countLoanedBook() } 현재 대여 중인 책의 권수를 반환하는 API이다. 현재 대여 중인 책의 권수를 반환한다. Service @Transactional(readOnly = true) fun countLoanedBook(): Int { return userLoanHistoryRepository.findByStatus(UserLoanStatus.LOA..