MVC 계층 구조
Model-View-Controller 의 약자로, 코드를 역할에 따라 세 부분으로 나누는 설계 패턴
원래 MVC는 웹 초창기에 화면(VIew)까지 서버에서 만들어서 보내주던 시절의 패턴이라고 한다.
요즘에는 View의 역할은 React 같은 프론트엔드가 대신하고, 내가 공부하고 있는 건 API 서버라 View는 없고 Controller와 Model만 사용한다.
- Controller : 클라이언트(HTTP)의 요청을 받아서 적절한 Service를 호출하고 응답을 돌려주는 역할 -> 교통정리
- Model
- Service : 실제 비즈니스 로직을 처리 -> 회원가입 시 이메일 중복체크, 비밀번호 암호화 등
- Repository : DB에 데이터를 저장하고 조회
클라이언트 → Controller → Service → Repository → DB
↓
클라이언트 ← Controller ← Service ← Repository
이렇게 나누는 이유는 각 계층이 자기 역할만 하도록 분리해서 코드 수정(유지보수)을 용이하게 하기 위함이다.
패키지 구조 만들기
com.seongmo.myshop 하위에 아래처럼 패키지를 만들자.
com.seongmo.myshop
├── MyshopApplication.java
└── member
├── MemberController.java
├── MemberService.java
└── MemberRepository.java
도메인(member, item 등) 별로 패키지를 묶는 방식이다.
나중에 상품, 거래 등 도메인이 추가될 때 동일한 구조로 확장하면 된다.
DTO 만들기
API 요청과 응답에 쓸 DTO를 먼저 만들자. 엔티티를 직접 반환하지 않고 DTO로 감싸는 것이 원칙이다.
DTO(Data Transfer Object) : 계층 간 데이터를 주고받을 때 필요한 데이터만 골라 담는 객체
member 패키지 하위에 dto 패키지를 만들고 두 파일을 생성하자.
com.seongmo.myshop
└── member
├── dto
│ ├── MemberJoinRequest.java
│ └── MemberJoinResponse.java
// MemberJoinRequest.java
package com.example.myshop.member.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class MemberJoinRequest {
private String email;
private String password;
private String nickname;
}
클라이언트가 회원가입 요청을 보낼 때 담아보내는 데이터이다.
아래와 같이 요청(JSON)을 보내면,
{
"email": "test@test.com",
"password": "1234",
"nickname": "성모"
}
Spring이 이 JSON을 MemberJoinRequest 객체로 변환해준다.
이 때, @Getter 는 Lombok이 각 필드의 getter 메서드를 자동으로 만들어주고,
@NoArgsConstructor 는 인자 없는 기본 생성자를 자동으로 만들어준다.
// MemberJoinResponse.java
package com.example.myshop.member.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class MemberJoinResponse {
private Long id;
private String email;
private String nickname;
}
위는 회원가입 처리 후 클라이언트에게 돌려줄 데이터이다.
password는 응답에 포함하면 안되니 빠져있다.
@AllArgsConstructor 는 모든 필드를 인자로 받는 생성자를 자동으로 만들어준다.
MemberController 만들기
// MemberController.java
package com.seongmo.myshop.member;
import com.seongmo.myshop.member.dto.MemberJoinRequest;
import com.seongmo.myshop.member.dto.MemberJoinResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/members")
public class MemberController {
private final MemberService memberService;
@PostMapping
public ResponseEntity<MemberJoinResponse> join(@RequestBody MemberJoinRequest request) {
MemberJoinResponse response = memberService.join(request);
return ResponseEntity.ok(response);
}
@GetMapping("/{id}")
public ResponseEntity<MemberJoinResponse> getMember(@PathVariable Long id) {
MemberJoinResponse response = memberService.getMember(id);
return ResponseEntity.ok(response);
}
}
@RestController 는 @Controller + @ResponseBody 로, 메서드가 반환하는 객체를 JSON으로 변환해서 응답한다.
@RequestMapping("/api/members") 는 이 컨트롤러의 모든 API 앞에 /api/members 가 붙는다는 의미이다.
@PathVariable 은 URL 경로의 값을 받아오는 것이다. /api/members/1 로 요청하면 id에 1이 들어온다.
@RequestBody 는 HTTP 요청의 Body에 담김 JSON을 Java 객체로 변환해준다.
MemberService 만들기
// MemberService.java
package com.seongmo.myshop.member;
import com.seongmo.myshop.member.dto.MemberJoinRequest;
import com.seongmo.myshop.member.dto.MemberJoinResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
@Service
@RequiredArgsConstructor
public class MemberService {
private final Map<Long, MemberJoinResponse> store = new HashMap<>();
private final AtomicLong sequence = new AtomicLong(1);
public MemberJoinResponse join(MemberJoinRequest request) {
Long id = sequence.getAndIncrement();
MemberJoinResponse member = new MemberJoinResponse(id, request.getEmail(), request.getNickname());
store.put(id, member);
return member;
}
public MemberJoinResponse getMember(Long id) {
return store.get(id);
}
}
JPA를 아직 공부하기 전이기 때문에 임시로 Map에 데이터를 저장하는 방식으로 구현했다. (JPA 공부 후 DB로 교체 예정)
Postman으로 API 테스트
Postman 프로그램으로 회원가입 테스트를 해보자.

위와 같이 회원가입 요청을 보내면 아래와 같이 응답이 온다.

GET 버튼으로 회원 정보도 얻을 수 있다.

'Backend > Spring' 카테고리의 다른 글
| [Backend/Spring] Spring Boot - @OneToMany 와 N+1 문제 (0) | 2026.03.29 |
|---|---|
| [Backend/Spring] Spring Boot - JPA 연관관계 (0) | 2026.03.29 |
| [Backend/Spring] Spring Boot - JPA와 MySQP 연동 (0) | 2026.03.29 |
| [Backend/Spring] Spring Boot - IoC/DI/Bean 개념과 MySQL 연결 (0) | 2026.03.29 |
| [Backend/Spring] Spring Boot - 프로젝트 생성과 구조 이해 (0) | 2026.03.29 |