우아한테크캠프 회고-JPA

11653 단어 회고회고

https://github.com/etff/jwp-qna/tree/etff

2번째 미션은 JPA를 활용한 리펙터링이다.
주어진 프로젝트에서 서비스 레이어에 복잡하게 얽혀있는 비지니스 로직을
도메인으로 이동한다. 복잡한 객체간의 관계를 JPA를 통해 맵핑한다.

개인적으로 orElse(null)은 좋아하지 않는다. null을 반환하는 것은 좋은 패턴이 아니기 때문이다. 이번에 리뷰를 통해 EntityNotFoundException을 알게되었다.
Entity가 없음을 명확하게 표현할 수 있어 좋다고 생각한다.


테스트를 하면서 값이 들어갔는지 확인이 필요했는데 실제 DB의 값이 들어갔는지 검증을 할 수 있었다.

이번 개발에서 계층형 테스트를 적극적으로 사용했다.

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class AnswerRepositoryTest {
    private User user1 = new User("tester", "password", "tester", "[email protected]");
    private Question question1 = new Question("title", "content");
    private Answer answer1 = new Answer(user1, question1, "content");

    @Nested
    @DisplayName("save 메서드는")
    class Describe_save {

        @Nested
        @DisplayName("답변 정보가 주어지면")
        class Context_with_answer extends JpaTest {
            final Answer expected = answer1;

            @Test
            @DisplayName("답변을 저장하고, 답변 객체를 리턴한다")
            void it_saves_answer_and_returns_answer() {
                Answer actual = getAnswerRepository().save(expected);

                assertThat(actual).isEqualTo(expected);
            }
        }
    }
}

추가적으로 기존 코드에서 Fixture로 공유되던 코드는 삭제했다. 테스트를 하기 위해 미리 만들어둔 Fixture가 편하기는 하나 나중에는 관리 포인트로 바뀐다. 또한 상태 공유시에 side effect를 고려해야한다.

이번 미션을 하면서 정적 팩터리 메서드가 유용한 사례를 찾았다.
의도를 드러내기 좋다.

    public static DeleteHistory ofQuestion(Long contentId, User deletedBy) {
        return new DeleteHistory(ContentType.QUESTION, contentId, deletedBy);
    }

    public static DeleteHistory ofAnswer(Long contentId, User deletedBy) {
        return new DeleteHistory(ContentType.ANSWER, contentId, deletedBy);
    }

자바 객체지향 미션을 할때 1급 컬렉션을 썼었는데 JPA에도 응용이 되었다.
N:1의 관계의 컬렉션을 1급 컬렉션으로 만들고 해당 로직을 분리하니 코드가 깔끔해지는 것을
경험하였다.

@Embeddable
public class Answers {
    @OneToMany(mappedBy = "question", cascade = CascadeType.ALL)
    private List<Answer> answers;

    protected Answers() {
        this.answers = new ArrayList<>();
    }

    public DeleteHistories delete(User loginUser) {
        DeleteHistories deleteHistories = new DeleteHistories();
        for (Answer answer : answers) {
            final DeleteHistory deleteHistory = answer.delete(loginUser);
            deleteHistories.addHistory(deleteHistory);
        }
        return deleteHistories;
    }

    public void add(Answer answer) {
        this.answers.add(answer);
    }

    public List<Answer> getAnswers() {
        return new ArrayList<>(answers);
    }
}

배운 것

좋은 웹페이지 즐겨찾기