팀 프로젝트는 저번 주에 끝났지만 밤샘 작업으로 인해 TIL을 작성하지 못했다. 하지만 그냥 넘기기에는 느낀 것들이 많아 기록하려고 늦게나마 작성해보려고 한다.
컨벤션 정리
GitHub 정리
코드 스타일 정리
파일 구조화 통일
네이밍 구조 통일
공통으로 사용할 클래스 구성 정리
머지 계획 구성
API명세서 구체화
기간 별 계획 구체화
동시 개발 계획 구성
머지 구간 세분화
코드 리뷰 디테일
컨벤션 확인(띄어쓰기)
API명세서 누락부분
네이밍 포인트
리뷰 받는 자는 설명할 수 있어야한다.(중요)
코드 수정 시 명세서 수정(팀원 공유)
제 3자가 봤을 때 이해하기 좋도록 가독성 좋은 코드 작성
이번 프로젝트 때 혼자가 아닌 여러사람의 코드를 통해 협업을 하게 됨으로써 준비하면 좋을 것들을 한 번 프로젝트가 끝나고서도 복기해보면서 적어보았다.
와이어 프레임을 초기에 잘 설정해 두었다.
머지 계획을 잘 구성하였다.
머지와 커밋하는 양이 많았다.
마감 시간 전날 밤을 새가면서 까지 완료했다는 점.
코드리뷰를 꼼꼼하게 하지 않았다는 점.
API명세서를 제대로 작성하지 않았고 명확한 목적과 결과가 없어 코드를 작성하면서도 추가적인 기능들이 붙는 바람에 머지하는 계획이 점점 미루어졌다.
코드 스타일과 개성이 너무나도 달랐지만 개인적이니 최대한 수용하려고 하였다.
코드는 계획이 전부였다.
코드를 작성하는데 가독성이 매우 중요했다.
기능이 있다고 한들 왜 쓰는지를 알아야하고 남한테 설명할 줄 알아야한다.
가장 중요하다고 생각하는 부분은 내 코드는 내가 알아야하고, 작성한 근거가 있어야한다.
지연 로딩으로 인해 추가 쿼리가 반복되는 상황에서, 쿼리 실행 시점에 어떤 연관 엔티티를 함께 로딩할지 미리 정의할 수 있다는 점을 배웠다.
성능 튜닝이 필요한 곳에서 “Eager로 바꿀까?” 고민하기 전에 적용해볼 수 있는 옵션이라는 걸 알게 됨.
JPA 기본 페치 전략만으로는 특정 조회 시 N+1 문제가 발생할 수 있음.
@EntityGraph는 “이번 조회는 이 테이블도 같이 가져와줘!” 라고 명시하는 방법.
Repository 메서드에 붙여서 동작함.
@EntityGraph(attributePaths = {"user", "comments"}) // 함께 조회할 연관 엔티티들
Optional<Post> findById(Long id);
현재 인증된 사용자(UserDetails)를 파라미터로 받을 때 사용.
커스텀 UserDetails 를 반환하도록 만들어 두면 더욱 깔끔하게 사용 가능.
@GetMapping("/profile")
public ResponseEntity<?> getProfile(@AuthenticationPrincipal CustomUserDetails user) {
Long userId = user.getId();
return ResponseEntity.ok("Hello " + userId);
}
flush()는 커밋과 다름. 커밋은 트랜잭션을 종료하지만, flush는 변경 내용을 DB에 적용만 하고 트랜잭션은 유지됨.
“영속성 컨텍스트 ↔ DB” 간의 동기화를 강제로 시키는 느낌으로 생각하면 이해가 쉬움.
user.setName("updated");
em.flush(); // 여기서 update 쿼리가 바로 나감
findBy + [필드명] + [조건] + And/Or + ...
Equality(=)
Boolean 필드
findByIsDeletedFalse()
findByApprovedTrue()
findByUsernameAndStatus(String username, Status status)
findByUserIdOrEmail(Long userId, String email)
findByAgeGreaterThan(int age)
findByCreatedAtLessThan(LocalDateTime time)
findByPriceBetween(int min, int max)
findByIdIn(List<Long> ids)
문자열 (Like / Containing / StartsWith / EndsWith)
Containing = LIKE %keyword%
StartsWith = keyword%
EndsWith = %keyword
findByTitleContaining(String keyword)
findByUsernameStartsWith(String prefix)
findByEmailEndsWith(String suffix)
findByDeletedAtIsNull()
findByDeletedAtIsNotNull()
findByUserIdOrderByCreatedAtDesc(Long userId)
findAllByOrderByIdAsc()
findFirstByOrderByCreatedAtDesc() // 최신 1개
findTop3ByLikesDesc() // 상위 3개
예시 1 — 유저의 ‘삭제되지 않은’ 최신 게시글 10개
findTop10ByUserIdAndIsDeletedFalseOrderByCreatedAtDesc(Long userId)
예시 2 — 제목에 keyword 포함 & published 상태
findByTitleContainingAndPublishedTrue(String keyword)
예시 3 — 생성일 조건 + 여러 사용자 포함
findByUserIdInAndCreatedAtAfter(List<Long> userIds, LocalDateTime time)
Spring Data JPA는 메서드 이름만으로도 쿼리를 자동 생성한다.
기본 구조는 findBy + 필드명 + 조건이며, And, Or, OrderBy 등으로 조합 가능.
자주 쓰는 조건들은 다음과 같음:
Containing, StartsWith, EndsWith
GreaterThan, LessThan, Between
In, IsNull, IsNotNull
IsDeletedFalse, ApprovedTrue 같은 boolean 조건
OrderBy + ASC/DESC
Top, First (limit)
튜터님의 피드백을 통해 컨밴션의 통일화와 코드리뷰의 중요성, API명세서가 얼마나 잘 짜여져 있는지에 따라서 계획의 차질이 많이 바뀌는 것을 느꼈고, 팀원과 충분한 소통과 코드 스타일을 타협함으로써 좋은 결과물을 얻을 수 있다는 것을 느낄 수 있는 첫 프로젝트 였습니다.
계기를 토대로 다음에 더 좋은 결과물을 얻을 수 있을 것이고 같은 실수를 안하기 위해 노력하겠습니다ㅎㅎ