Skip to content

Conversation

@soonvro
Copy link

@soonvro soonvro commented Jan 31, 2025

공부한 내용

Q. Controller는 뭘까요?

자바 스프링 MVC 3요소 중 하나인 Controller는 클라이언트의 1)요청을 받아 2)기능을 수행 후 3) 사전에 정의한 형태로 반환합니다.
스프링에서 자주 사용한는 두가지 Controller:

  • @Controller: 스프링에서 프론트엔드까지 담당하는 경우, View를 통해 화면을 렌더링 후 반환합니다.
  • @RestController: 스프링에서 API만 담당하는 경우, 정의한 데이터 형식으로 Response를 반환합니다.

Q. Controller는 왜 인터페이스가 없어도 될까요?

Controller는 인터페이스를 구현함으로써 얻는 이익이 적습니다.

  • Controller의 메서드는 URL과 매핑되며, 구현 코드 내에서 직접 호출하는 경우가 적어 다형성을 사용할 기회가 적습니다.
  • Controller는 클라이언트와 소통하는 end-point이며, 다른 객체에 DI되어 사용하는 경우가 없어 인터페이스의 이점이 적습니다.
  • 중복되는 로직을 줄이고 싶은 용도에는 추상 클래스를 사용하여 구현합니다.

Q. Service는 뭘까요?

자바 스프링에서 Service비즈니스 로직을 담당하는 계층입니다.

  • 입출력(Controller)과 데이터 접근(Repository) 사이의 중간 계층을 담당합니다.
  • 여러 Repository와 API를 이용하여 Controller가 요청한 기능을 수행합니다.

Q. Repository는 뭘까요?

자바 스프링에서 Repository데이터베이스를 추상화하기 위한 인터페이스/클래스입니다.

  • CRUD와 같은 기본적인 데이터 로직을 제공합니다.
  • SQL 등의 쿼리를 숨기고 개발자가 직관적으로 이해할 수 있는 인터페이스를 제공합니다.
  1. Service vs Repository
    • Repository는 “순수 데이터 접근” 담당합니다.
    • Service 계층이 “비즈니스 로직, 트랜잭션 제어, 여러 Repository 결합” 등을 담당합니다.
  2. 단일 책임 원칙
    • 보통 하나의 Entity에 하나의 Repository를 둡니다.
  3. 영속 계층 캡슐화
    • 외부(Controller 등)에서 직접 DB 접근 X, 반드시 Service를 통해 Repository를 호출
  4. Repository가 다른 Repository를 직접 호출 X
    • Service 계층이 여러 Repository를 조합하는 것이 바람직

Q. Entity가 뭘까요?

Entity의 사전적 의미는 개념이나 정보 단위와 같은 현실 세계의 개체입니다. 하나 이상의 속성을 가질 수 있습니다.
자바 스프링에서 Entity는 데이터베이스에서 지속될 수있는 데이터를 나타내는 POJO(Plain Old Java Object)입니다.


Q. DTO가 뭘까요?

DTO(Data Transfer Object)는 계층 간 주고받는 데이터 형태의 약속입니다.

  • 클라이언트 ↔ 서버 간 요청·응답이나, 서버 내부의 특정 계층 간 데이터 이동(메서드 파라미터/결과) 시, 비즈니스 로직이나 엔티티(Entity)와 분리된 순수 데이터를 담는 객체입니다.

DTO의 기능

  1. 데이터 담기
    • 단순히 필드 + 게터/세터(or Lombok) 형태로, 전송에 필요한 속성만 정의.
  2. 검증 로직
    • 일부 Validation 어노테이션, 단순 포맷 검증.
  3. Convert/Mapping
    • 컨트롤러나 서비스에서 DTO ↔ 엔티티 변환이 필요하면, DTO 클래스가 toEntity(), static factory 메서드 등 제공하는 경우도 있음.
    • 또는 별도의 Mapper/Converter를 둘 수도 있음.

Best Practice

  1. 엔티티(Entity)와 명확히 분리
    • 엔티티 클래스와 DTO 클래스를 물리적으로 다른 패키지에 두거나, 이름을 명확히 구분 (User, UserDto).
    • 직접 엔티티를 Request/Response로 사용하지 않는 것이 일반적 (외부 노출 시 안정성/유연성 문제).
  2. 필요한 필드만 노출
    • “만능 DTO” 만들지 말고, 해당 API나 작업에 필요한 필드만 갖춘 “맞춤 DTO”를 설계.
    • 예: UserCreationDto, UserResponseDto, UserUpdateDto 등 용도별로 분리 가능.
  3. Validation 어노테이션 적극 활용
    • @NotNull, @Min, @Size 등 Bean Validation으로 입력값을 사전에 검증.
    • Controller 레벨에서 @Valid로 처리 후, Service로 넘김.
  4. MapStruct / ModelMapper / 수동 변환
    • DTO ↔ 엔티티 변환이 많다면, 매퍼 라이브러리(예: MapStruct)를 도입하여 자동화할 수 있음.
    • 단순 소규모 프로젝트이면 수동 변환(“DTO: toEntity(), “Entity: toDto()”)으로 충분.
  5. 읽기 전용/쓰기 전용 DTO 구분
    • “조회용” DTO(UserDetailResponse)와 “생성/수정 요청” DTO(UserRequest)를 나누면 관리가 쉽고 안전함.

Anti-Pattern

  1. 엔티티 그대로 Request/Response로 사용
    • 외부에 DB 구조/컬럼 노출 위험,
    • 엔티티 필드 변경 시 API 스펙이 깨질 수 있음 → 유지보수성 악화
  2. DTO가 중복 로직 과도
    • 한 DTO에 모든 비즈니스 시나리오를 커버하려다 필드가 불필요하게 많아짐(“God DTO”).
    • 읽기 전용, 쓰기 전용이 섞여 복잡도 상승.
  3. 비즈니스 로직을 DTO에 넣음
    • DTO는 데이터 전송이 주목적, 복잡한 비즈니스 규칙은 Service/도메인 영역이 맡아야 함.
    • DTO에 복합 로직이 들어가면 계층 혼란.
  4. 빈약한 Validation
    • 전송 객체임에도 불구하고 유효성 검증을 하지 않거나, 위치가 애매해 진 경우.
    • 결과적으로 Controller나 Service에서 매번 수동 체크 → 중복 코드 증가.
  5. Mapping 혼재
    • Controller도 map, Service도 map, repository도 map 등… DTO ↔ 엔티티 변환 중복 발생.
    • 명확한 Mapper(혹은 DTO 메서드) 한 곳에서 담당하는 것이 좋음.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants