이벤트 컨트롤러 부분 코드를 해석해보려고 한다
이 EventController는 이벤트(프로모션) 관리 API를 모아둔 스프링 컨트롤러입니다
🔹 /api로 시작하는 URL로 들어오는 요청을 받아서
🔹 이벤트 조회/종료/등록을 처리해
"이 코드가 뭘 하는지 / 어떻게 동작하는지"를 한 번 분석해보겠다
1) 이 클래스가 하는 역할 한 줄 요약
관리자는 이벤트를 등록/종료/전체조회를 할 수 있고,
일반 사용자는 현재 진행 중인 이벤트를 조회할 수 있게 만든 REST API 컨트롤러이다
🔹 @RestController : JSON 응답을 반환하는 컨트롤러
🔹 @RequestMapping("/api") : 이 컨트롤러의 모든 API는 /api로 시작
2) 주입(Autowired)된 것들이 뭐 하는 애들인지
@Autowired private BrevoClient brevoClient;
@Autowired private BrevoService brevoService;
@Autowired private EventService eventService;
@Autowired private ReviewService reviewService;
@Autowired private AccountService accountService;
▪ EventService : 이벤트 DB 조회/등록/수정(종료) 같은 "핵심 비즈니스" 실행
▪ ReviewService : 여기서는 "리뷰"라기보다 이미지 파일 검증/저장 기능을 재사용하고 있음
(파일 크기 체크, 확장자 체크, 저장하고 URL 반환)
▪ AccountService : 이메일 받을 회원 목록 가져오기
▪ BrevoService/BrevoClient : 이벤트 등록되면 메일 발송 (Brevo = 메일 발송 서비스)
3) 설정값(@Value)으로 받는 것들
@Value("${resource.path}")
private String resourcePath;
@Value("${resource.event.prefix}")
private String eventPrefix;
▪ resourcePath : 서버에 이미지 저장할 실제 경로(ex: C:/.../static/ 같은 물리 경로)
▪ eventPrefix : URL prefix (ex: /images/event/)
즉, 업로드한 파일을 resourcePath에 저장하고, 브라우저에서 접근할 URL은 eventPrefix로 만든다는 뜻
4) API 1) 관리자: 이벤트 전체 조회
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/event/all")
public ResponseEntity<Map<String, Object>> getEvent(EventDTO eventDTO){
eventDTO.setCondition("SELECT_ALL_EVENT");
List<EventDTO> list = eventService.getEventList(eventDTO);
return ResponseEntity.ok(Map.of("eventDatas", list));
}
동작흐름
1. 관리자만 접근 가능(@PreAuthorize("hasRole('ADMIN')"))
2. eventDTO.condition = "SELECT_ALL_EVENT"
3. eventService.getEventList() 호출 ➡ 전체 이벤트 조회 쿼리 실행
4. 결과를 "eventDatas": list로 JSON 반환
{
"eventDatas": [ ...이벤트목록... ]
}
🔼 반환 예시
5) API 2) 관리자: 이벤트 종료 요청
@PreAuthorize("hasRole('ADMIN')")
@PatchMapping("/admin/event/{eventPk}/end")
public ResponseEntity<Map<String, Object>> endEvent(
@PathVariable Integer eventPk,
EventDTO eventDTO
)
이 API가 하려는 것
특정 이벤트를 "종료 처리"하는 API이다
URL로 eventPk를 받아서 업데이트
흐름
1. 관리자 권한 체크
2. URL의 (eventPk}를 eventPk 변수로 받음
3. DTO에 pk 주입 + condition 지정 🔽
eventDTO.setEventPk(eventPk);
eventDTO.setCondition("UPDATE_END_EVENT");
4. eventService.updateEvent(eventDTO) 실행 : 실패하면 404 + 에러 메시지
5. 성공하면 eventPk랑 eventEndDate를 내려줌
{
"code": "VALIDATION_ERROR",
"message": "이벤트를 찾을 수 없습니다.."
}
✅ 실패 응답
{
"eventPk": 3,
"eventEndDate": "2026-02-18"
}
✅ 성공 응답(현재 코드 기준)
6) API 3) 전체: 현재 진행중인 이벤트 조회
@GetMapping("/all/event/in-progress")
public ResponseEntity<?> mainEvent(EventDTO eventDTO){
eventDTO.setCondition("SELECT_ALL_PROGRESS_EVENT");
List<EventDTO> list = eventService.getEventList(eventDTO);
if (list==null || list.isEmpty()) {
return ResponseEntity.status(404).body(Map.of(
"code", "NO_ACTIVE_EVENT",
"message", "현재 진행중인 이벤트가 없습니다."
));
}
return ResponseEntity.ok(Map.of("eventDatas", list));
}
동작
1. 누구나 접근 가능 (관리자 제한 없음)
2. condition으로 "진행중인 이벤트만 조회"하게 함
3. 결과 없으면 404 + 에러 코드/메시지
4. 있으면 eventData로 반환
여기서 "진행중"이란 보통 DB 기준으로
시작일 <= 오늘
종료일 >= 오늘 (또는 종료일이 null이면 진행중)
같은 조건을 쿼리에서 걸었을 가능성이 크다
7) API 4) 관리자: 이벤트 등록 (이미지 업로드 + DB 저장 + 이메일 발송)
@PreAuthorize("hasRole('ADMIN')")
@PostMapping("/admin/event")
public ResponseEntity<Map<String, Object>> insertEvent(
@RequestPart("eventImage") MultipartFile eventImage,
@ModelAttribute EventDTO eventDTO
)
이 부분이 제일 핵심이라고 볼 수 있다
요청 형태
🔹 @RequestPart("eventImage") ➡ multipart/form-data로 파일 업로드 받는다
🔹 @ModelAttribute EventDTO eventDTO ➡ 폼 데이터로 넘어온 나머지 필드들을 EventDTO에 바인딩
즉 프론트는 대충 이런 형태로 파일을 보내는 것이다
▪ eventImage 파일
▪ eventName, eventStartDate, eventEndDate, ... : 문자열 폼 필드들
흐름 (try 안에서 순서대로)
1) 이미지 존재 체크
없거나 비어있으면 400
{ "code": "MISSING_IMAGE", "message": "이미지를 불러오지 못하였습니다." }
2) 파일 사이즈 체크
reviewService.checkFileSize(eventImage)
실패하면 400 + 최대 허용 크기 안내
⚠️ 메시지에 "kb"라고 적어놨는데, getAllowedImageMaxBytes()가 진짜 bytes면 단위가 안 맞을 수도 있음.
3) 확장자 체크
reviewService.checkFileExtention(eventImage)
실패하면 400 + 허용 확장자 안내
4) 파일 저장 + URL 얻기
String eventImageUrl = reviewService.saveImageAndGetUrl(resourcePath, eventPrefix, eventImage);
eventDTO.setEventImageUrl(eventImageUrl);
▪ 서버 폴더에 이미지 저장
▪ 접근 가능한 URL 만들어서 DTO에 세팅
5) 이벤트 DB INSERT
eventDTO.setCondition("INSERT_EVENT");
if (!eventService.insertEvent(eventDTO)) { ... }
6) (중요) 이메일 발송은 “실패해도” 이벤트 등록 성공으로 처리
try {
List<AccountDTO> emails = accountService.getEmailDatas();
brevoService.sendEventMailToAllAgreeAsync(emails, eventDTO);
} catch (Exception e) { e.printStackTrace(); }
▪ 이메일 발송은 별도 try-catch로 감싸서 터져도 무시
▪ sendEventMailToAllAgreeAsync니까 비동기로 보내는 구조
7) 최근 등록된 eventPk 다시 조회
eventDTO.setCondition("SELECT_ONE_EVENT_PK_RECENT");
eventDTO = eventService.getEvent(eventDTO);
방금 insert한 이벤트의 PK가 필요해서 “가장 최근 이벤트 PK”를 조회
여기서 주의점
⚠️ “최근” 기준이 애매하면 동시 등록이 있을 때 꼬일 수 있어.
⚠️ 보통은 INSERT 후 생성된 PK를 바로 받는 방식(MyBatis useGeneratedKeys / JdbcTemplate KeyHolder)이 더 안전함.
8) 성공 응답
{
"code": "success",
"message": "이벤트 등록 성공",
"eventPk": 123
}
9) 바깥 catch
JSON 변환 실패/파일 저장 실패 같은 예외 → 400 BAD_REQUEST
'🎅 오너먼트 프로젝트' 카테고리의 다른 글
| 오너블리 프로젝트 GitHub 코드 가져와 Eclipse에 Import하기 (0) | 2026.02.19 |
|---|---|
| Node 설치 (0) | 2026.02.19 |
| DTO 멤버변수 타입 래퍼타입으로 일치시키기 (0) | 2026.02.14 |
| 자바와 쿼리문이 합쳐져 있던 Repository 파일 MyBatis로 결합도 낮춰보기 (0) | 2026.02.11 |
| 람다식 BeanPropertyRowMapper로 코드 줄여보기 (0) | 2026.02.07 |