HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
♥️
2기 최종 프로젝트 팀별 공간
/
🐳
[팀 07] 머구리(Meoguri)
/
🎱
WorkSpace
/
🛢️
Coding Conventions/Namings/Architecture Rules
🛢️

Coding Conventions/Namings/Architecture Rules

Coding Conventions

주석

클래스 위 - Documentation Comments

/** * 영속성 계층에서 리포지토리를 사용하여 단순한 조회로직을 담당하는 컴포넌트를 'Query' 라고 지정한다. * - Query 는 클래스에 적용한다. */ public @interface Query { }
Documentation Comments example @Query
  • <br>, <pre>, <li> 등의 html 태그는 사용하지 않음

메서드 위 - Block Comments, Single-Line Comments

/* 좋아요 숫자 업데이트 */ void updateLikeCount(long bookmarkId, ReactionType existedType, ReactionType requestType); /* 태그 목록 조회 */ List<GetUsedTagWithCountResult> getUsedTagsWithCount(long profileId);
Single-Line Comment example @BookmarkService
/** * 리액션 요청 * - 사용자는 북마크에 대해 하나의 reaction 만을 가질 수 있다. * - 사용자는 reaction 을 등록, 취소, 변경 할 수 있다. * like, hate 요청 reactionType -> like, hate * 등록 0 0 like 1 0 * 취소 1 0 like 0 0 * 변경 0 1 like 1 0 */ void requestReaction(ReactionCommand command);
Block-Comment example @ ReactionService.requestReaction
  • 인터페이스의 메서드에는 주석 필수, 적절한 한글 네이밍 제공
  • @Override 메서드의 경우 인터페이스에 주석이 있으므로 생략 가능
  • 혹은 더 구체적인 비즈니스 로직에 대한 주석을 Block-Comment 로 작성 가능

코드 내부 - Single-Line Comments

@Override public void requestReaction(ReactionCommand command) { ... /* 리액션 요청 */ final ReactionType existedType = profile.requestReaction(bookmark, requestType); /* 북마크 좋아요 숫자 업데이트 */ bookmarkService.updateLikeCount(bookmarkId, existedType, requestType); }
Single-Line Comments @ReactionServiceImpl.requestReaction( )

테스트 코드

  • //given, //when, //then 의 경우 에만 End-Of-Line Comments 사용
  • 그 외에는 /* */ - Single-Line Comments 사용

컨트롤러 계층

  • 매핑 메서드 인자 작성 방식 아래와 같이 통일
/* 북마크 등록 */ @PostMapping public Map<String, Object> registerBookmark( final @AuthenticationPrincipal SecurityUser user, final @RequestBody RegisterBookmarkRequest request ) { return Map.of("id", bookmarkService.registerBookmark(request.toCommand(user.getProfileId()))); }
BookmarkController.registerBookmark
 
  • 하나의 컨트롤러에서 하나의 서비스에만 의존
    • 예외) 프로필 사진 등록을 위한 ProfileService의 S3Uploader 의존
    • 예외) 프로필 사진에서 태그 서비스, 북마크 서비스 등 의존 - 리팩토링중
  • 조회 요청의 경우 서비스에서 조회해온 Result 데이터를 Response 데이터로 변환 하는 중간에 new-line 하나 두기
 

서비스 계층

  • @Service 는 항상 @Transactional(readonly=true) 와 함께 클래스를 선언한다
    • CUD 메서드의 경우 메서드에 @Transactional 추가
 
 

영속성 계층

  • 애그리거트 하나당 하나의 리포지토리를 제공한다.
 

Domain.entity

  • model 로 이름 바꾸고 싶음
  • vo 패키지 없애고 싶음

테스트 코드

  • //given //when //then 주석 사용
  • 계층별 테스트 작성시 BaseXXXTest 사용
    • e.g.) BaseControllerTest, BaseServiceTest
    • BaseXXXTest의 한글 셋업 메서드를 사용해 테스트 데이터 셋업하기
    • 검증 대상이 되는 메서드의 호출은 셋업 메서드를 사용하지 않는다.
    • 한글 검증 메서드를 사용해 테스트 데이터를 검증 할 수 있다.
 
  • 검증 방식
    • assertThat/ isEqaulTo를 기본으로 사용
    • 가독성을 고려하여 다른 메서드 적절하게 사용
      • e.g.) extracting 시리즈, isTrue
    • 검증이 덩어리로 처리 되는 경우 assertAll 사용해 한번에 검증
 
  • @Nested
    • 테스트 이름의 prefix는 @Nested 클래스와 통일
    • depth 는 최대 하나
    • @Nested 를 사용하는 테스트의 경우 외부 클래스의 @BeforeEach 에서 데이터 셋업을 하지 않는다.
      • 중복 코드가 발생하면 setUpInternal과 같은 방식으로 우회하고 테스트 메서드 별로 클래스 분리를 고려할것
       
  • 테스트 이름 = 테스트_메서드_이름 (+ 테스트_결과) (+ 이유)
    • 테스트_메서드_이름
      • 서비스 혹은 리포지토리의 인터페이스 간편 이름
      • 간편 이름이 없거나 표현이 너무 길어저 메서드 이름이 충분히 메서드의 의도를 드러내는 경우 영문 메서드 명을 그대로 사용
    • 테스트 결과가 항상 성공이거나 굳이 필요 없는 경우 생략
    • e.g.) 북마크_생성_성공, 북마크_생성_실패_이름이_너무_김
    •  
  • 반환값의 변수명은 메서드의 앞글자 따기
    • e.g.) registedId = bookmakrService.registerBookmark(…)
  • 일련의 동일한 타입의 변수/필드를 선언 할때 숫자를 가장 뒤에 붙일 것
    • e.g. ) user1Id X → userId1 O
 
  • 테스트 순서는 성공 먼저, 실패
  • 테스트의 전반적인 순서는 프로덕션 코드의 선언 순서와 통일하기
  • controller test
    • mockMvc 검증 시 when, then 절 ResultActions 를 추출하여 구분해 작성
    • doPrint 항상찍기

Common

 
  • final 키워드 - 필요한 곳에 항상 사용
  • 컬렉션(e.g. TagIdsFavoriteCategories …) 이 아닌 vo의 경우
    • enum (e.g. ReactionType, OpenType, Category, …)
    • 별도의 table로 구분되지 않고 사용되는 필드 vo (e.g. Email, Link)
    • 전역적으로 사용한다 (Controller Layer - Service Layer - Persistence Layer)
  • 예외 처리
    • 버그 및 공격성 예외
      • → LinkoceanRuntimeException + 예외 메시지 발생
      • → CustomRestControllerAdvice.handleBadRequestException( )
      • → 400 BadRequest + 메시지 무시
    • 입력 예외
      • → IllegalArgumentException + 예외 메시지 발생
      • → CustomRestControllerAdvice.handleIllegalArgumentException( )
      • → 400 BadRequest + 예외 메시지
 

Namings

도메인

영어
한글
bookmark
북마크
user
사용자
profile
프로필
linkmetadata
링크 메타데이터
notification
알림
tag
태그
follow
팔로우
favorite
즐겨찾기

비즈니스 용어

  • 컨트롤러, 서비스 계층 에서 활용
영어
한글
컨트롤러 메서드 사용 예
register
등록
registerBookmark, registerProfile
getXXXs
목록 조회
getMyBookmarks, getAllCetegories, getProfiles
getDetailed
상세 조회
getDetailedBookmark, getDetailedProfile
update
수정
updateBookmark, updateProfile
remove
삭제
removeBookmark
follow/unfollow
팔로우/언팔로우
follow/unfollow
favorite/unfavorite
즐겨찾기/ 즐겨찾기 취소
favorite/unfavorite
login/loginSuccess
로그인/로그인 성공
login/loginSuccess
share
공유
shareNotification
obtain
얻기
getOrSaveLinkmetadataTitle

영송성 계층의 용어

영어
한글
리포지토리 메서드 사용 예
find
조회
findByProfile_idAndBookmark
save
저장
save
update
수정
update
delete
삭제
deleteByProfileAndBookmarkAndType
exists
존재 확인
existsByOwner_idAndBookmark
count
카운트 조회
countReactionGroup
XXXInternal, XXX
  • 항상 쌍으로 제공 되어야 하며 XXXInternal은 XXX를 위한 로직을 Jpa QueryMethod 혹은 Jpql을 사용해 제공한다. XXX는 XXXInternal을 사용해 default 메서드로 간단한 변환작업만을 수행하여 제공한다.
 
findXXXFetchYYYAndZZZ
  • 애그리거트 루트 XXX를 조회하며 연관 YYY, ZZZ를 fetch 조인 한다

@Query

  • 영속성 계층에서 리포지토리를 사용하여 단순한 조회로직을 담당하는 컴포넌트를 'Query' 라고 지정한다.
  • 리포지토리를 사용한 조회가 상대적으로 단순하지만 재활용이 많이 되는 경우 Query 를 활용한다
    • Find,Check + [도메인] + [Query]
    • e.g. FindBookmarkByIdQuery, CheckIsFollowQuery
  • 리포지토리를 사용한 조회가 복잡하여 로직을 분리 시키는 것이 적절하다고 생각될때 Query 를 활용한다.
    • [도메인] + [Query]
    • e.g.) , ReactionQuery
 

DTO

컨트롤러 계층

요청(Request)

[비즈니스 용어] + [도메인] + [Request]
  • 비즈니스 용어, 도메인 만으로 구분되지 않는 경우 컨트롤러 메서드 이름 + [Request]

응답(Response)

[도메인] + [Response]
  • 도메인 만으로 응답이 구분되지 않는경우 컨트롤러 메서드 이름 + [Response]

서비스 계층

명령 (Command)

  • [비즈니스 용어] + [도메인] + [Command]
  • 데이터의 변경을 요구하는 경우 사용

결과 (Result)

  • [비즈니스 용어] + [도메인] + [Result]
  • 조회성 요청에 대한 응답으로 사용한다

영속성 계층

조건 (Cond - Condition)

  • 조회의 필터링 조건
  • 서비스 계층에서도 활용가능 하다
  • e.g.) BookmarkFindCond, ProfileFindCond

페이저블 (pageable)

  • Spring 의 PageRequest 사용
  • 컨트롤러 계층에서도 활용 가능 하다

페이지 (Page)

  • Spring 의 PageImpl 사용
  • 서비스 계층에서도 활용 가능 하다

Common

  • 메서드 명
    • checkXXX : void를 반환하며 인자에 대한 검증으로 사용 - 예외 터트림
      • e.g.) Preconditions.checkNotNull(), Preconditions.checkArgument()
  • 값 객체의 필드명
    • 단일 필드인 경우 value
    • 컬렉션 필드인 경우 values 사용
    •  
 

Architecture Rules

계층형 구조

프로필/북마크 조회 시 join 처리

같은 애그리거트 toMany
루트 엔티티에서 참조 방식
조회 데이터 매핑
@Column profile → favoriteIds
Set<Vo> (id = bookmarkId)
페치 조인
@EmbeddedId profile → followIds
Set<Vo> (id = followerId + followeeId)
페치 조인
@Column bookmark → reaction
Set<Vo> (profileId + ReactionType)
페치 조인
@Column profile → favorieCategories
Set<Vo> (Category)
조회 될 일 없음 ??
다른 애그리거트 toOne
루트 엔티티에서 참조 방식
조회 데이터 매핑
@ManyToOne bookmark → profile (writer)
엔티티 - Profile writer
페치 조인
@Column bookmark → linkMetadata
아이디 - Long linkMetadataId
서비스에서 말아줌
@Column bookmarkTag → tag
아이디 - long tagId
서비스에서 말아줌
 

도메인 서비스가 필요한 기능 (여러 애그리거트에 걸친 CUD 요청)

  • 현재 도메인 서비스로 동작 중인 기능 (계층 구분은 안 되어 있음)
    • 프로필 등록 - 사용자 테이블에 외래키 물려주기
    • 북마크 등록/수정 - 태그 테이블에 데이터 추가
  • TODO
    • 북마크 상세 조회 - 북마크 조회 기록 테이블에 데이터 추가/변경
      • → 이벤트 처리가 적절할 듯?
    • 북마크 수정/삭제 - 즐겨찾기 테이블에 데이터 삭제