Controller의 역할
프로젝트 구조
└── com.example.korea_sleepTech_springboot
├── controller
│ ├── UserController.java
│ ├── AuthController.java
│ └── AdminController.java
├── service
│ ├── UserService.java
│ ├── AuthService.java
│ └── AdminService.java
└── dto
└── user / auth / response / request ...
1. 웹 요청의 진입점(Entry Point)
Controller는 클라이언트가 전송한 HTTP 요청을 가장 먼저 받아들이는 **프론트 컨트롤러(FE Controller)**이다.
- 어떤 경로(URL)로 어떤 동작을 할 것인지를 선언하고
- 요청을 적절한 Service 계층에 위임하며
- 응답을 포장해 클라이언트에게 다시 반환한다
2. Controller 분리 이유
단일 책임 원칙 (SRP: Single Responsibility Principle)
- AuthController: 로그인/회원가입/이메일 인증 등 인증(Authentication), 인가(Authorization) 중심 기능
- UserController: 로그인한 유저의 개인정보 조작 (조회, 수정, 탈퇴 등)
- AdminController: 어드민 권한이 있는 사용자의 역할 승격/강등 기능
즉, 기능별로 책임을 명확히 나누어 설계하면 유지보수성과 확장성이 극대화된다.
3. Spring MVC 구조
Spring은 DispatcherServlet이 요청을 가로채고 적절한 Controller에 매핑한다.
[요청]
↓
DispatcherServlet
↓
HandlerMapping → UserController, AuthController 등
↓
Service → Repository
↓
응답 반환
4. REST API 설계 원칙
- 리소스 단위로 URL을 설계하고
- GET, POST, PUT, DELETE로 HTTP 메서드 활용
- GET /api/v1/user/me: 내 정보 조회
- PUT /api/v1/admin/promote: 관리자 승격
- POST /api/v1/auth/signup: 회원 가입예시:
5. 보안 연동
- 인증된 사용자 → @AuthenticationPrincipal
- 관리자만 접근 → @PreAuthorize
- 인증은 JWT, SecurityContext, FilterChain 등을 통해 동작하고, Controller는 이를 활용만 하면 됨
UserController: 인증된 사용자 전용 API
- @AuthenticationPrincipal은 Spring Security에서 현재 인증된 사용자의 이메일을 주입
- @Valid는 유효성 검사 → DTO에 @NotNull 등의 애너테이션이 있으면 자동 체크됨
- ResponseEntity.ok()를 통해 HTTP 200 상태와 JSON 응답을 반환
- Service 로직에만 집중하도록 컨트롤러에서는 흐름만 정의
@RestController
@RequestMapping(ApiMappingPattern.USER_API) // /api/v1/user
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
private static final String GET_USER_INFO = "/me";
private static final String PUT_USER_INFO = "/me";
private static final String DELETE_USER = "/me";
@GetMapping(GET_USER_INFO)
public ResponseEntity<ResponseDto<GetUserResponseDto>> getUserInfo(
@AuthenticationPrincipal String userEmail
) {
ResponseDto<GetUserResponseDto> response = userService.getUserInfo(userEmail);
return ResponseEntity.ok(response);
}
@PutMapping(PUT_USER_INFO)
public ResponseEntity<ResponseDto<GetUserResponseDto>> updateUserInfo(
@AuthenticationPrincipal String userEmail,
@Valid @RequestBody UserUpdateRequestDto dto
) {
ResponseDto<GetUserResponseDto> response = userService.updateUserInfo(userEmail, dto);
return ResponseEntity.ok(response);
}
@DeleteMapping(DELETE_USER)
public ResponseEntity<ResponseDto<Void>> deleteUser(
@AuthenticationPrincipal String userEmail
) {
userService.deleteUser(userEmail);
return ResponseEntity.noContent().build();
}
}
AuthController: 인증/가입 관련 공개 API
- 인증 관련 요청은 대부분 POST 방식 사용: 민감 정보 (비밀번호 등)를 URL에 노출시키지 않기 위함
- Spring WebFlux의 Mono<>를 사용하는 API도 있어 → 비동기 응답 처리
- API 패턴이 명확해서 프론트엔드에서도 어떤 요청을 어디로 보내야 할지 혼동이 없음
@RestController
@RequestMapping(ApiMappingPattern.AUTH_API) // /api/v1/auth
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
private final MailService mailService;
@PostMapping("/signup")
public ResponseEntity<ResponseDto<UserSignUpResponseDto>> signup(@Valid @RequestBody UserSignUpRequestDto dto) {
ResponseDto<UserSignUpResponseDto> response = authService.signup(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}
@PostMapping("/login")
public ResponseEntity<ResponseDto<UserSignInResponseDto>> login(@Valid @RequestBody UserSignInRequestDto dto) {
ResponseDto<UserSignInResponseDto> response = authService.login(dto);
return ResponseEntity.ok(response);
}
@PostMapping("/send-email")
public Mono<ResponseEntity<String>> sendEmail(@Valid @RequestBody SendMailRequestDto dto) {
return mailService.sendSimpleMessage(dto.getEmail());
}
@GetMapping("/verify")
public Mono<ResponseEntity<String>> verifyEmail(@RequestParam String token) {
return mailService.verifyEmail(token);
}
@PostMapping("/reset-password")
public Mono<ResponseEntity<String>> resetPassword(@Valid @RequestBody PasswordResetRequestDto dto) {
return authService.resetPassword(dto);
}
}
AdminController: 관리자 전용 API
- @PreAuthorize("hasRole('ADMIN')"): Spring Security의 메서드 레벨 보안
- 해당 어노테이션이 없으면 누구나 접근 가능함 → 보안상 치명적
- /promote, /demote는 REST의 의미를 살려서 PUT 사용
@RestController
@RequiredArgsConstructor
@RequestMapping(ApiMappingPattern.ADMIN_API) // /api/v1/admin
public class AdminController {
private final AdminService adminService;
private static final String PUT_AUTHORITY_TO_ADMIN = "/promote";
private static final String PUT_AUTHORITY_DEMOTE = "/demote";
@PreAuthorize("hasRole('ADMIN')")
@PutMapping(PUT_AUTHORITY_TO_ADMIN)
public ResponseEntity<ResponseDto<PromoteToAdminResponseDto>> promoteUserToAdmin(
@RequestBody PutAuthorityRequestDto dto
) {
ResponseDto<PromoteToAdminResponseDto> response = adminService.promoteUserToAdmin(dto);
return ResponseEntity.ok(response);
}
@PreAuthorize("hasRole('ADMIN')")
@PutMapping(PUT_AUTHORITY_DEMOTE)
public ResponseEntity<ResponseDto<DemoteFromAdminResponseDto>> demoteUserFromAdmin(
@RequestBody PutAuthorityRequestDto dto
) {
ResponseDto<DemoteFromAdminResponseDto> response = adminService.demoteUserFromAdmin(dto);
return ResponseEntity.ok(response);
}
}
'웹개발 > SpringBoot' 카테고리의 다른 글
| 보안과 권한 시스템 핵심 - 5. filter/JwtAuthenticationFilter (1) | 2025.06.06 |
|---|---|
| 보안과 권한 시스템 - 4. Service/ServiceImpl (0) | 2025.06.05 |
| 보안 인증과 권한 시스템 - 2. Repository (2) | 2025.06.03 |
| 보안 인증과 권한 시스템 - 1. Entity (1) | 2025.06.02 |
| Springboot로 게시판(Post&Comment)만들기 - 두 테이블 연결 (0) | 2025.06.01 |