아래의 링크는 NEXTSTEP ATDD 과정을 모두 정리해놓은 Github 주소입니다. 필요한 분들을 한번씩 참고해보세요 :)
2023년도 새로운 해가 시작된 1월부터 시작된 NEXTSTEP의 ATDD 과정이 3월 말이 돼서야 모두 완주했다. 코드숨을 듣고 NEXTSTEP을 바로 듣게 된 계기는 향로 멘토님의 추천이었지만, 개인적으로 필요하다고 느꼈던 수강 목적은 다음과 같다.
사실 테스트 관련 멘토링을 또 듣는 것을 약간 망설인 시기도 있었지만, 과정을 수료하고 위의 3가지 목적을 모두 달성한 것 같아서 정말 잘 들었다고 생각했다.
일단 가장 찜찜했었던 부분이 테스트 코드 관련 부분이었는데, 이번 과정을 통해 나만의 테스트 전략을 확실하게 정하게 된 계기가 되었다.
관련된 내용은 NEXTSTEP 5주차 - 테스트 리팩토링, 그 속으로에서 처음으로 나오는 “멀고 험난한 리팩터링, 안전하게라도 하자”라는 문단에서 작성해놓았다.
또한 블랙박스 테스트의 보호 속에서 복잡한 비즈니스 로직을 어떻게 읽기 쉬운 코드로 리팩토링하는지를 많이 연습해 본 경험이었다.
지금까지 작성했던 NEXTSTEP의 각 주차 회고에 첫 주제들은 항상 내가 느낀 점들을 작성한 후, 학습한 내용들을 적어 내려갔다. 하지만 강의에 모든 내용을 적지는 않았으며, 강의 자료를 그대로 작성하기보다는 내가 따로 추가 학습하면서 이해했던 생각의 흐름(?)대로 써보았다.
실제로 강사님은 내가 정리해놓은 내용들 그 이상으로 되게 많은 꿀팁들과 자료들을 주셨다. 하지만 저작권(?)의 문제도 있을 것이며, 그런 꿀팁들을 내가 아직 완벽하게 누구한테 설명할 정도로 습득하진 않은 상태라서 말을 아꼈다.
앞으로 개발을 계속해나가면서 배운 내용들을 체화하면 그때 나의 관점에서 열심히 작성해서 공유해볼 생각이다.
이제 코드숨 총 정리때 처럼 피드백을 받은 내용과 문제를 분석하면서 추가로 학습한 내용들을 모두 정리해보며 다시 한번 리마인드하는 시간을 가져보려고 한다.
@JsonCreator
을 사용하거나 기본 생성자를 같이 생성해놓으면 된다.@JsonCreator
: 기본 생성자 대신 지정해준 생성자를 가지고 역직렬화를 진행하라고 알려주는 역할HttpMessageNotReadableException
이 발생하며 요청 데이터를 제대로 역직렬화 하지 못함[디미터 법칙에 대해 오해하고 있었던 부분을 다시 한번 되짚었던 계기]
A
라는 한 엔티티 객체에서 @Embedded
를 통해 일급 컬렉션 객체를 가지고 있었다. 그 일급 컬렉션 객체의 메서드를 사용하기 위해 Service 로직에서는 A.get일급컬렉션객체().메서드()
를 사용하였다.
머릿속에서는 관련 데이터를 가지고 있는 객체에 직접 변경 요청을 하라는 Tell, Don't Ask 원칙이 생각났기 때문이다. 하지만 이렇게 A 객체의 내부 구현에 직접적으로 의존한다면 추후에 리팩토링을 할 때 문제가 생길 수 있다는 걸 알게되었다.
따라서 외부 모듈에서 일급 컬렉션의 메서드를 사용할 때는 A 객체의 메서드를 통해서만 메시지를 주고받도록 하고, 생성한 A 객체의 메서드는 그대로 일급컬렉션 메서드로 위임하도록 하였다.
객체 자기 자신의 메서드 사용
메서드의 파라미터로 넘겨받은 객체의 메서드 사용
메서드 내부에서 생성 및 초기화한 인스턴스(객체)의 메서드 사용
해당 객체가 인스턴스 변수로 가지고 있는 객체의 메서드 사용
(나는 위의 사례를 해당 방식으로 수정하였다)
// 디미터 법칙을 준수하는 4가지 방법 예시
public class Member {
private Gender gender;
public void addMember(Member member) {
}
public void updateMember(Age age) {
age.get(); // 2. 메서드의 파라미터로 넘겨받은 객체의 메서드 사용 (O)
}
public void Demeter_법칙을_잘지키기() {
addMember(new Member()); // 1. 객체 자기 자신의 메서드 사용 (O)
Name name = new Name("ALEX");
name.getFullName(); // 3. 메서드 내부에서 생성 및 초기화한 객체의 메서드 사용 (O)
gender.get(); // 4. 인스턴스 변수로 가지고 있는 객체가 소유한 메서드 사용 (O)
}
}
ApplicationListener<ContextRefreshedEvent>
를 구현(implement)하여 onApplicationEvent()
메서드에 데이터를 생성하는 로직 호출@BeforeEach
메서드에 데이터를 생성하는 로직 호출LazyInitializationException
)getxxxxList()
를 사용하면 추후에 반환 타입 변경이 일어날 시 수정 포인트가 늘어날 수 있음getFavoriteList()
→ favorites()
CascadeType.REMOVE
vs orphanRemoval = true
CasecadeType.REMOVE
나 orphanRemoval = true
모두 부모 엔티티를 삭제하면 자식 엔티티도 삭제한다.CasecadeType.REMOVE
: 부모 엔티티에서 자식 엔티티의 관계를 제거하더라도 DELETE 문이 나가지 않는다.orphanRemoval = true
: 부모 엔티티에서 자식 엔티티의 관계를 제거하면 자식은 고아로 취급되어 그대로 사라진다 (DELETE 발생)@ElementCollection
& @CollectionTable
엔티티의 일부 속성을 컬렉션으로 매핑할 때 사용
값 타입의 라이프 사이클은 엔티티를 따라간다
CollectionTable
name
: 값 타입을 저장할 별도의 테이블 명
joinColumns
: 참조할 fk 컬럼명 (보통 소속된 엔티티의 pk값)
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(
name = "MEMBER_ROLE", // 값 타입 용 별도 테이블 명
joinColumns = @JoinColumn(name = "id", referencedColumnName = "id") // id랑 fk
)
@Column(name = "role")
private List<String> roles;
생성되는 테이블 관계
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html
application-{profile}.yml
형식으로 로드하려고 시도한다{xxxx}
)의 속성 이름은 kebab-case(항상 소문자만 사용)를 사용하여 참조해야 Spring Boot가 relaxed binding 기능을 사용해서 센스 있게 프로퍼티 이름을 자동으로 변환해줄 수 있음@Value
나 @ConfigurationProperties
로 설정값을 바인딩 할 때demo.item-price
O | demo.itemPrice
X@Value("${demo.item-price}")
는 설정 파일에서 demo.item-price
및 demo.itemPrice
양식과 시스템 환경의 DEMO_ITEMPRICE
를 가져옵니다.@Value("${demo.itemPrice}")
를 사용하면 demo.item-price
및 DEMO_ITEMPRICE
가 고려되지 않습니다.key={$key}
형태가 되면 안됨 → db.url=${db.url}
: 오류db.url
)을 해석하려고 할 때, 그 값은 동일한 키와 연관된 값인 ${db.url}
로 대체db.url
의 속성 값을 확인하려고 할 때 ${db.url}
을 동일한 키와 연결된 값인 ${db.url}
로 대체 (db.url
←→ ${db.url}
: 무한 루프 발생)
메일 보냈는데 시간 괜찮으실 때 확인부탁드릴게요!