상세 컨텐츠

본문 제목

[입문 필독] Spring 계층 구조 예제와 자주 하는 실수 정리

Developer/Backend

by 웰크 2025. 6. 12. 13:54

본문

728x90

Spring Boot 계층 구조 완벽 가이드: Controller, Service, Repository

Spring Boot로 웹 애플리케이션을 개발할 때 가장 먼저 이해해야 할 개념은 "계층 구조(Layered Architecture)"입니다. 이 글에서는 Controller, Service, Repository의 역할을 명확히 구분하고, 초보자가 자주 하는 실수와 그 해결법까지 상세히 설명합니다.




📌 왜 계층을 나누는가?

소프트웨어 아키텍처에서 계층을 나누는 이유는 책임 분리(SRP)유지보수성 향상에 있습니다. 각 계층은 다음과 같은 책임을 갖습니다:

  • Controller: 외부 요청을 받고 응답을 반환
  • Service: 비즈니스 로직 처리
  • Repository: DB와의 데이터 접근 처리 (JPA 사용)



🧩 각 계층의 역할 및 자주 하는 실수

🧭 Controller 계층

API 엔드포인트를 정의하고, 요청(Request)을 받아 Service 계층으로 위임한 후 응답(Response)을 클라이언트에 반환합니다.

🚫 잘못된 예: Controller가 Repository를 직접 호출

@RestController
@RequestMapping("/members")
public class MemberController {
    private final MemberRepository memberRepository; // 잘못된 의존

```
@PostMapping
public ResponseEntity<Void> create(@RequestBody Member member) {
    memberRepository.save(member); // 비즈니스 로직 누락
    return ResponseEntity.ok().build();
}
```

}

문제점: Controller가 비즈니스 로직을 직접 처리하면, 재사용성과 테스트성이 떨어지고, 로직이 분산되어 유지보수가 어려워집니다.

✅ 올바른 구조: Repository는 Service가 호출하며, Controller는 Service를 통해 기능을 수행합니다.

@RestController
@RequestMapping("/members")
public class MemberController {
    private final MemberService memberService;

```
@PostMapping
public ResponseEntity<Void> create(@RequestBody MemberCommand.Create request) {
    memberService.createMember(request);
    return ResponseEntity.ok().build();
}
```

}

⚙️ Service 계층

비즈니스 로직을 처리하는 중심 계층입니다. 트랜잭션 처리, 유효성 검사, 도메인 로직 수행 등을 이곳에서 담당합니다.

실수: 비즈니스 로직 없이 단순히 Repository만 호출하는 "Pass-through Service"

public class MemberService {
    private final MemberRepository memberRepository;

```
public void createMember(MemberCommand.Create request) {
    memberRepository.save(request.toEntity());
}
```

}

개선: 도메인 로직 추가, 예외 처리, 유효성 검증 등 책임을 명확히 부여합니다.


💾 Repository 계층

JPA 기반으로 DB 접근을 담당하는 계층입니다. 도메인 객체(Entity)를 저장하고 조회합니다.




🧱 왜 각 계층마다 Command와 Model을 나누는가?

계층마다 입력과 출력을 명확히 나누면, 각 계층의 역할이 분명해지고 변경에 유연해집니다.

  • Command 객체: 외부 요청을 받는 데이터 구조 (ex. MemberCommand.Create)
  • Model 객체: 비즈니스 로직에서 사용되는 객체
  • Entity: DB에 저장되는 구조

이렇게 나누지 않으면, Controller에서 받은 JSON을 그대로 Entity로 변환하거나, Entity를 바로 반환하는 등 보안/유지보수/계약 측면에서 문제가 발생합니다.




🧩 계층 구조 예시: 회원 등록 API

아래는 Spring Boot의 전형적인 계층 구조 예시를 보여주는 코드입니다.


1. MemberCommand.Create

public class MemberCommand {
    public record Create(String name, String email) {
        public Member toEntity() {
            return new Member(name, email);
        }
    }
}

2. Member Entity

@Entity
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

```
protected Member() {}

public Member(String name, String email) {
    this.name = name;
    this.email = email;
}
```

}

3. MemberRepository

public interface MemberRepository extends JpaRepository<Member, Long> {
}

4. MemberService

@Service
@RequiredArgsConstructor
public class MemberService {
    private final MemberRepository memberRepository;

```
@Transactional
public void createMember(MemberCommand.Create command) {
    Member member = command.toEntity();
    memberRepository.save(member);
}
```

}

5. MemberController

@RestController
@RequiredArgsConstructor
@RequestMapping("/members")
public class MemberController {
    private final MemberService memberService;

```
@PostMapping
public ResponseEntity<Void> create(@RequestBody MemberCommand.Create request) {
    memberService.createMember(request);
    return ResponseEntity.ok().build();
}
```

}

이 예시를 통해 계층 구조가 실제로 어떻게 작동하는지 구체적으로 이해할 수 있습니다.




📌 마무리

계층 구조는 단순히 폴더 구조만을 의미하지 않습니다. 각 계층의 책임을 명확히 구분함으로써, 유지보수성과 테스트 용이성을 확보할 수 있습니다. 초보일수록 잘못된 구조에 빠지기 쉬우니, 컨트롤러에서 직접 레포지토리를 호출하는 패턴은 반드시 피해야 합니다.

728x90
반응형

관련글 더보기