🦁멋쟁이사자처럼 백엔드 부트캠프 13기 🦁
TIL 회고 - [103]일차
🚀103일차에는 각자 만든 기능 브랜치들의 병합을 위해 Git Projects로 팀원들의 이슈를 관리할 수 있도록 구축해보았다. 또한 게시판 Board 담당으로 게시글 관련 엔티티 및 Repository, Service, Controller를 설계하였다. (2025.05.13)
103일차 기능개발 Log
- 깃허브 - 프로젝트 - 이슈생성




- 이슈번호 #15 확인 (본인이 만든 이슈번호 확인)
git switch develop
- 현재 브랜치가 develop 브랜치인지 확인
git pull
- 원격의 develop 브랜치 작업내용 가져오기 - 팀원들이 병합한 코드 최신화
git checkout -b feature/#15
- 아까 만든 이슈번호로 새 브랜치 생성
- 최신화된 develop 브랜치 내용을 기반으로 feature/#15브랜치가 분기
git branch
- (현재 어느 브랜치에 위치해있는지 확인
- [* feature/#15] 처럼 표시됨
- feature/#15 브랜치 확인 후 기능 개발 진행
git status
git add .
git commit -m “feat: 댓글 엔티티 설계 (#15)”
- 기능 개발 진행 후 (#15)처럼 이슈번호를 적어야 이슈트래킹 용이
git push --set-upstream origin main
- 원격 저장소에 feature/#15 브랜치와 함께 feature/#15 브랜치 작업내용을 push
- 기능 개발이 끝났으면 저장소에서 Compare & Pull Request로 눌러 PR 날리기
Board 게시판 개발
Board - Controller - 게시글 작성
- 웹에서 사용자가 새 게시글을 작성할 때, 프론트엔드(웹 페이지나 앱)에서 백엔드 서버로 보내는 요청을 받아서 처리하는 부분
@PostMapping // HTTP POST 요청 처리
public ResponseEntity<BoardResponseDto> createBoard(
@Valid @RequestBody BoardRequestDto boardRequestDto, // 요청 본문을 BoardRequestDto 객체로 매핑하고 유효성 검사 수행
@AuthenticationPrincipal UserDetails userDetails // 현재 인증된 사용자의 UserDetails 객체 주입
) {
// 프로젝트의 UserDetails 구현체에서 사용자 ID(Long)를 String으로 반환한다고 가정합니다.
Long userId = Long.parseLong(userDetails.getUsername()); // UserDetails에서 사용자 ID 가져와 Long으로 변환
// BoardService의 createBoard 메소드 호출하여 게시글 생성 비즈니스 로직 수행
BoardResponseDto createdBoard = boardService.createBoard(boardRequestDto, userId);
// 생성된 게시글 정보(DTO)와 함께 HTTP 상태 코드 201 (Created) 반환
return ResponseEntity.status(HttpStatus.CREATED).body(createdBoard);
}
- 게시글 작성 API : POST /api/v1/boards
- @param boardRequestDto : 게시글 생성 요청 데이터 (JSON 형태)
- @param userDetails : 인증된 사용자의 정보 (Spring Security 제공)
- @return : 생성된 게시글 정보와 함께 HTTP 상태 코드 201 (Created) 반환
- @throws ResourceNotFoundException : Space (공간) 또는 Host (공간 공유자)를 찾을 수 없을 때 발생
- @throws AccessDeniedException : 사용자가 호스트가 아니거나 권한이 없을 때 발생
- @PostMapping 클라이언트(ex. 웹 브라우저)가 /api/v1/boards 라는 주소로
새로운 데이터를 생성해달라는 요청을 보낼 때, 이 createBoard 메소드가 실행 - public ResponseEntity<BoardResponseDto> createBoard(...)
: 반환 타입이 ResponseEntity<BoardResponseDto>인 객체로
Spring에서 HTTP 응답을 유연하게 다룰 수 있게 해주는 객체
응답 본문(BoardResponseDto)과 HTTP 상태 코드(ex. 성공 201, 에러 400 등)을 함께 보낼 수 있음 - BoardResponseDto
: 게시글 생성 요청이 성공했을 때, 클라이언트에게 돌려줄 게시글 정보를 담는 데이터 형태(DTO) - @Valid
: BoardRequestDto 객체에 설정된 유효성 검사 규칙(ex. 제목은 비어있으면 안됨)을 자동으로 실행해주는 어노테이션
만약 유효성 검사에 실패하면 Spring이 에러 응답을 자동으로 보내 메소드 내부에서 별도로 검사할 필요가 없음 - @RequestBody
: 클라이언트가 HTTP 요청의 본문(body)에 담아 보낸 데이터를 BoardRequestDto 객체로 자동으로 변환(매핑) 후 boardRequestDto 변수에 넣음
(ex. 클라이언트가 JSON 형태로 게시글 제목, 내용 등을 보내면 Spring이 BoardRequestDto 객체로 만들어줌) - @AuthenticationPrincipal UserDetails userDetails
- @AuthenticationPrincipal : Spring Security를 통해 현재 로그인(인증)된 사용자의 정보를 가져와서
userDetails 변수에 넣어주는 어노테이션 - UserDetails
: Spring Security에서 사용자 정보를 나타내는 표준 인터페이스
객체 안에 로그인한 사용자의 아이디, 비밀번호, 권한 등의 정보가 담김 - Long userId = Long.parseLong(userDetails.getUsername());:
- userDetails.getUsername(): UserDetails 객체에서 사용자의 식별자(ex. 아이디 / 이메일)를 문자열(String) 형태로 가져옴
- Long.parseLong(...): 가져온 문자열 형태의 사용자 ID를 숫자(Long) 타입으로 변환
- 현재 게시글을 작성하려는 사용자의 ID를 userId 변수에 저장 - BoardResponseDto createdBoard = boardService.createBoard(boardRequestDto, userId);:
실제로 게시글을 데이터베이스에 저장하고 비즈니스 로직을 처리하는 부분은 BoardService라는 다른 클래스에 위임
- boardService.createBoard() 메소드 호출
: 클라이언트로부터 받은 게시글 정보(boardRequestDto)와 현재 로그인한 사용자의 ID(userId)를 넘김
- BoardService에서 게시글을 성공적으로 생성하면, 그 결과를 BoardResponseDto 형태로 받아 createdBoard 변수에 저장 - return ResponseEntity.status(HttpStatus.CREATED).body(createdBoard);:
- 게시글 생성 비즈니스 로직이 성공적으로 완료되면, 클라이언트에게 응답을 반환
- ResponseEntity.status(HttpStatus.CREATED): HTTP 상태 코드를 201 (Created, 생성됨)으로 설정
(새 데이터가 성공적으로 생성되었음을 의미하는 표준 상태 코드) - .body(createdBoard):
- 응답 본문에 BoardService로부터 받은 createdBoard 객체(생성된 게시글 정보)를 담아서 보냄
정리 :
클라이언트로부터 새 게시글 작성 요청(POST)이 오면, 요청 본문에 담긴 게시글 정보와 현재 로그인한 사용자의 정보를 받아서, Service 계층에 게시글 생성을 요청하고, 서비스에서 성공적으로 게시글을 만들면 생성된 게시글 정보와 함께 '생성됨(201)' 상태 코드를 클라이언트에게 응답으로 돌려주는 역할을 하는 메소드
❓AWS S3에서의 이미지 업로드 방식 차이점
2가지 방식 : Multipart vs Presigned URL
1. Multipart 이미지 업로드 (클라이언트 ➡️ 서버 ➡️ S3)
- 동작 방식
1) 클라이언트(ex. 브라우저) 이미지 파일을 HTTP 요청의 본문(multipart/form-data)에 담아
2) 애플리케이션 서버로 보냄
3) 서버는 이 파일을 받아 S3 SDK를 사용하여 S3 버킷으로 업로드 - 장점
- 구현이 간단함 (클라이언트 쪽에서는 일반적인 파일 업로드 요청만 보내면 되는 구조)
- 서버에서 파일 내용을 직접 처리하므로 업로드 전 서버에서 추가적인 유효성 검사/가공(ex. 리사이징, 워터마크 추가)이 용이 - 단점
- 대용량 파일이나 많은 수의 파일이 동시에 업로드될 경우, 서버가 파일 데이터를 직접 처리 및 S3로 전송하므로 서버 부하 증가
➡️서버의 확장성(Scalability)에 병목 가능성
- (클라이언트 ➡️ 서버, 서버 ➡️ S3) 처럼 두단계이므로 업로드 시간이 길어질 수 있음
- 서버에 AWS 인증 정보가 필요
2. Presigned S3 이미지 업로드 (클라이언트 ➡️ S3 직접)
- 동작 방식
1) 클라이언트가 파일 자체를 서버로 보내지 않고 (올릴 수 있는 권한이 담긴 임시 URL)을 요청
2) 서버는 그 요청을 받아 S3 SDK를 사용하여 특정 버킷의 특정 경로에 파일을 업로드할 수 있는 Presigned URL을 생성
3) 클라이언트는 응답받은 이 Presigned URL을 사용하여 S3 버킷에 파일을 직접 업로드 - 장점
- 서버가 파일 데이터를 직접 처리하지 않고 Presigned URL만 생성해주므로, 서버 부하없이 효율적
- 대용량 파일 업로드나 트래픽이 많은 상황에서의 서버 확장성에 용이
- 업로드 시간이 단축 (클라이언트 ➡️ S3 한 단계)
- 클라이언트는 AWS 인증 정보 없이 서버로부터 받은 Presigned URL만으로 안전하게 S3에 접근 가능
- 임시 URL에 제한된 권한과 유효 기간 설정이 가능해 보안성이 높음 - 단점
- 클라이언트 쪽에서 Presigned URL을 받아 S3에 직접 업로드하는 로직 구현이 필요
- 업로드 전 서버에서 파일 내용을 기반으로 하는 유효성 검사나 가공이 어려움
➡️가공을 위해서는 업로드 후 S3 이벤트 처리
정리 :
- 큰 규모의 서비스 및 대용량 파일 업로드나 높은 트래픽이 예상된다면 Presigned S3 이미지 업로드 방식
- 개발 초기 단계이거나 파일 크기가 작고 트래픽이 많지 않다면, 구현이 간단한 Multipart 이미지 업로드 방식으로 시작 후
서비스 규모가 커지면 Presigned URL 방식으로 전환
- 블로그 서비스처럼 사용자가 직접 이미지를 업로드하는 경우, Presigned URL 방식도 가능
✅만약 LoCo 프로젝트에 적용한다면 소규모의 회원이 이용하는 프로젝트이므로
호스트가 공간 정보 등록/수정 시에만 이미지를 업로드하게 됨
✅이럴경우 구현이 더 간단한 Multipart 이미지 업로드 방식을 선택하는 것도 방법
LoCo 프로젝트 - S3 Multipart 기술 도입 테스트
- 호스트 게시판에 이미지 도입

- 버킷 이름이과 S3 객체 키 (data/image/abc-123.jpg)를 통해 이미지의 접근을 공개로 허용한 경우에는
- 이미지 URL이
https://버킷이름.s3.ap-northeast-2.amazonaws.com/data/image/abc-123.jpg
와 같은 형태 - 클라이언트는 이 URL을 사용하여 이미지를 로드
액터 (Actor)
- 웹 애플리케이션 개발에서 현재 요청을 수행하는 사용자(Actor)와 관련된 정보를 캡슐화하여 편리하게 사용하기 위한 패턴
(ex. User Rq) - Rq (Request): 웹 요청 자체
- User Rq: 현재 웹 요청을 보내고 있는 사용자(User)와 관련된 정보를 담고 있는 객체
- 액터 (Actor): 시스템과 상호작용하는 주체 (ex. 현재 로그인하여 요청을 보내는 사용자)
액터 패턴의 목적
- 컨트롤러나 서비스 메소드를 개발할 때,
현재 요청을 보낸 사용자가 누구인지, 어떤 권한을 가지고 있는지, 해당 사용자의 ID는 무엇인지 등의 정보가 자주 필요 - 이러한 사용자 정보를 얻기 위해 매번
@AuthenticationPrincipal UserDetails userDetails를 메소드 인자로 받거나, SecurityContextHolder.getContext().getAuthentication().getPrincipal()와 같은 코드를 사용하는 것은
반복적이고 비효율적인 코드 - "User Rq" 또는 "액터" 객체 도입 시 이 객체 하나만 주입받거나 접근하여 현재 사용자의 모든 관련 정보를 쉽게 얻을 수 있음
// 기존 방식 (UserDetails 사용)
public ResponseEntity<?> createBoard(@Valid @RequestBody BoardRequestDto boardRequestDto, @AuthenticationPrincipal UserDetails userDetails) {
Long userId = Long.parseLong(userDetails.getUsername()); // UserDetails에서 ID 가져와 변환
// ... 비즈니스 로직 ...
}
// User Rq / Actor 객체 사용 방식 (예시)
public ResponseEntity<?> createBoard(@Valid @RequestBody BoardRequestDto boardRequestDto, RqUser rqUser) { // RqUser 객체 주입
Long userId = rqUser.getUserId(); // RqUser에서 사용자 ID 바로 가져옴
boolean isHost = rqUser.isHost(); // RqUser에서 권한 확인
User currentUser = rqUser.getUser(); // RqUser에서 User 엔티티 가져옴 (구현 방식에 따라 다름)
// ... 비즈니스 로직 ...
}
액터 패턴의 장점
- 코드 간결화 : 컨트롤러 및 서비스 메소드가 간결해짐, 사용자 정보를 얻는 코드가 반복되지 않아 효율적
- 정보 캡슐화 : 사용자 ID, 권한, 심지어 연관된 User 엔티티 객체까지 하나의 객체에 담아 관리 가능
- 유지보수 용이성: 사용자 정보를 가져오는 로직이 변경되어도 "User Rq" 객체 내부만 수정
액터 패턴의 단점
- 초기 구현 비용 : "User Rq" 객체를 정의하고, 요청 스코프 빈으로 등록하거나
Spring MVC의 Argument Resolver를 구현하여 컨트롤러 메소드 인자로 주입되도록 설정하는 초기 작업이 필요
- 추상화 레이어 추가 : 시스템에 새로운 객체가 추가되므로 낯선 구조가 될 수 있음
액터 패턴의 도입 가능성 (LoCo 프로젝트)
- 게시글 작성은 호스트만 가능
- 공간 등록/수정은 해당 공간의 호스트만 가능
- 예약 확인은 게스트와 호스트 모두 관련 예약에 대해서만 가능
- 관리자 기능은 관리자만 접근 가능
- 이처럼 사용자 역할과 권한에 기반한 로직이 많을 경우
현재 요청을 수행하는 사용자의 정보를 중앙에서 관리하고 쉽게 접근할 수 있는 "User Rq" 또는 "액터" 객체 패턴을 도입하여
코드의 가독성과 유지보수성을 향상 가능
- 특히 @AuthenticationPrincipal UserDetails userDetails에서
userId를 Long.parseLong(userDetails.getUsername()) 형태로 변환하는 과정이 반복되는 것을 줄이고,
사용자 역할(GUEST, HOST, ADMIN)을 확인하는 헬퍼 메소드(rqUser.isHost(), rqUser.isAdmin()) 등을
객체 내에 포함시켜 편리하게 사용 가능
정리 :
아직 프로젝트 규모가 작고 사용자 역할 구분이 단순하다면 필수는 아닐 수 있지만 사용자 역할이 나뉘고 권한 관리가 필요한 서비스에서는 "User Rq" 또는 "액터" 객체 패턴 도입을 생각해볼 수는 있음
🚀회고 결과 :
Github Project로 템플릿을 작성하여 개발일지 및 이슈사항에 대해 팀원들과 공유할 수 있는 환경을 만들 수 있었음
게시판을 개발하기 위한 Board 엔티티에 관련해 Controller, Repository, Service 등 구현을 할 수 있었음
이미지 업로드를 위한 AWS S3 기술에 대해 공부해볼 수 있었음
'Recording > 멋쟁이사자처럼 BE 13기' 카테고리의 다른 글
| [멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_113일차_"2차 팀 프로젝트 마무리" (0) | 2025.12.08 |
|---|---|
| [멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_108일차_"DB 마이그레이션 및 중간 피드백" (0) | 2025.12.06 |
| [멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_102일차_"Git 협업 및 CI 구축" (0) | 2025.11.06 |
| [멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_101일차_"2차 팀 프로젝트 : 공유 공간 매칭" (0) | 2025.08.19 |
| [멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_100일차_"2차 팀 프로젝트 시작" (0) | 2025.05.22 |