- 프로젝트 명 : 판 떼기
- 소개
- 한 줄 정리 : 트렐로를 모티브한 일정관리 서비스
- 내용 : 트렐로를 모티브한 일정관리 서비스
- 진행 날짜 : 2024.12.23 ~ 2024.12.31
프로젝트 요약
1. 프로젝트 목적
:신입 2년차 백엔드 개발자 채용 공고에서 요구하는 핵심 기술 역량을 실전 프로젝트를 통해 학습하고, 이를 기반으로 개발 역량을 검증하는 것이 목표
-
- 백엔드 개발 기술 습득
- Java & Spring Boot 기반의 REST API 개발
- JPA & MySQL을 활용한 데이터 모델링 및 트랜잭션 관리
- Spring Security & JWT 기반 인증/인가 구현
- AWS 클라우드 환경에서의 배포 경험
- EC2 + Docker + Docker Compose로 컨테이너화된 애플리케이션 배포
- RDS(MySQL) 분리를 통한 데이터 관리 효율성 향상
- CI/CD 자동화 학습(GitHub Actions 연동)
- S3를 활용한 파일 업로드 스토리지 구축
- 협업 및 코드 품질 향상
- AOP(Aspect Oriented Programming) 및 Interceptor를 활용하여 중복 코드 제거
- Redis를 활용한 동시성 문제 해결 및 성능 개선
- 코드 리뷰 및 기능 설계
- 백엔드 개발 기술 습득
2. 프로젝트 구현 (가이드)
<필수 구현>
- 회원가입/로그인
- 이메일 형식 ID와 Bcrypt 암호화된 비밀번호 사용
- JWT 인증 및 권한(일반 유저/관리자) 설정
- 예외처리 : 중복 가입, 유효성 검사 실패 시 오류 반환
- 멤버 및 역할 관리
- 워크스페이스 내 멤버 초대 및 역할 부여
- 권한에 따라 기능 접근 제한 적용
- 워크스페이스 : CRUD / 초대된 멤버만 접근 가능
- 보드 : CRUD / 보드 내부에서 리스트 및 카드 관리
- 리스트 : CRUD / 리스트 순서 변경 기능 지원
- 카드 : CRUD
- 검색 : 카드 제목, 내용, 담당자 기반 검색 / 특정 보드에 속한 모든 카드 검색
- 댓글 : CRUD / 댓글 작성자만 수정, 삭제 가능
- 첨부파일 : AWS S3 파일 업로드(jpg, png, pdf, csv) / 파일 크기 제한(5MB) 및 삭제 기능 제공
- 알림 : 카드 변경, 멤버 추가, 댓글 작성 시 실시간 알림 기능 / 슬랙 혹은 디스코드
- 배포
- 코드 변경 시, 자동으로 빌드 및 테스트를 수행하는 CI 파이프라인을 구성
- 테스트가 성공적으로 완료되면 프로덕션에 자동 배포되도록 CD 파이프라인을 설정
- CI/CD를 활용해 애플리케이션의 빌드, 테스트, 배포 과정을 자동화해 개발 효율성과 안정성을 높임
<도전 구현>
- 최적화(Indexing)
- 카드 검색 속도 향상을 위한 인덱스 적용
- 쿼리 속도 분석 및 성능 개선
- 동시성 제어(Concurrency Control)
- 카드 이동 시 동시성 충돌 방지
- 낙관적 락, 비관적 락, 분산 락을 활용하여 데이터의 일관성과 무결성 유지
- 캐싱(Caching)
- Redis를 활용한 조회수 캐싱
- 조회수는 매일 자정에 자동 초기화
사용한 기술 및 개념
& 맡은 역할
1. 백엔드
- Spring Boot : RESTful API 개발 및 비즈니스 로직 구현
- Spring Security + JWT : 인증 및 권한 관리(Bcrypt 비밀번호 암호화)
- JPA & Hibernate : ORM 기반 데이터 관리(Cascade 활용)
- MySQL(RDS) : 데이터 모델링 및 성능 최적화(Indexing)
2. 인프라 & 배포
- AWS EC2 : 애플리케이션 배포
- AWS S3 : 첨부파일 저장소
- AWS RDS(MySQL) : 데이터베이스 호스팅 및 성능 최적화
- 데이터베이스 호스팅 : AWS RDS에서 제공하는 DB를 사용
- Docker & Docker-Compose : 컨테이너화 및 로컬 개발 환경 통합
- CI/CD (GitHub Actions + EC2 배포) : 자동 빌드 및 배포 파이프라인 구축
3. 데이터 관리 & 최적화
- Redis : 조회수 캐싱, 동시성 제어(Locking), 리프레시 토큰 관리
- 트랜잭션 관리 : @Transactional을 적용하여 데이터 일관성 유지
4. Tool
- GitHub & GitHub Actions : 코드 버전 관리 및 CI/CD 자동화
- Postman : API 테스트 및 문서화
- IntelliJ IDEA : 개발 환경 설정 및 코드 작성
- Lombok : @Getter, @NoArgsConstructor 등 코드 간결화
5. 팀 내에서 맡은 역할
- 워크스페이스(Workspace)
- 워크스페이스 생성, 수정, 삭제, 조회 기능 구현
- 워크스페이스 멤버 초대 및 역할 관리 기능 추가
- 리스트(List)
- 리스트 생성, 수정(순서 변경 포함), 삭제, 조회 기능 구현
- GreenHopper 알고리즘을 활용한 리스트 정렬 및 순서 변경 기능 구현
- 첨부파일(File)
- AWS S3를 활용한 파일 업로드 및 삭제 기능 구현
- MultipartFile 처리 및 파일 형식 검증 로직 추가
- 파일 조회 API 구현
팀에서 구현한 내용
& 내가 구현한 내용
1. 프로젝트 설계
-와이어 프레임
-ERD 작성
-API 명세서 작성
https://documenter.getpostman.com/view/18429295/2sAYJ4gzPD
트렐로
The Postman Documenter generates and maintains beautiful, live documentation for your collections. Never worry about maintaining API documentation again.
documenter.getpostman.com
-코드 컨벤션
- 코드 스타일 및 네이밍 규칙
- 클래스 및 메서드 네이밍
- 클래스명 : PascalCase 사용
- 메서드명 : camelCase 사용
- 패키지 구조 : 도메인별 패키지 구조, 공통 기능은 common 패키지에 정리
- 클래스 및 메서드 네이밍
- DTO 및 Entity 변환 방식
- Entity -> ResponseDto 변환 : Entity를 ResponseDto로 변환할 때는 생성자를 활용
- RequestDto -> Entity 변환 : RequestDto를 Entity로 변환할 때는 spread 방식 사용
- Controller - Service 간 데이터 전달 방식
- RequestDto 직접 전달 금지 : Controller에서 RequestDto를 바로 Service로 넘기지 않음 -> 개별 필드로 전환 후 전달
- Service -> Controller 데이터 반환 방식 : Service는 ResponseDto를 반환하고, Controller에서 CommonResponseDto로 감싸서 반환
- 공통 응답 DTO 사용
- 단일 응답(CommonResponseDto)
- 리스트 응답(CommonListResponseDto)
- Service -> Controller 데이터 반환 시 항상 DTO 사용
- Controller에서는 CommonResponseDto 또는 CommonListRepsonseDto로 감싸서 반환
//단일 응답
public class CommonResponseDto<T> {
private final String message;
private final T data;
public CommonResponseDto(String message, T data) {
this.message = message;
this.data = data;
}
}
//리스트 응답
public class CommonListResponseDto<T> {
private final String message;
private final List<T> data;
public CommonListResponseDto(String message, List<T> data) {
this.message = message;
this.data = data;
}
}
- Enum 사용 규칙
- 대문자로만 작성하고, value 값 없이 사용
- 모든 상태 값은 Enum으로 관리
- 서비스 인터페이스 사용 금지
- Service interface 생성하지 않고, 바로 구현 클래스를 사용
- Lombok 사용 규칙
- @AllArgsConstructor, @Setter 사용 금지
- Getter 및 RequiredArgsConstructor 사용
- 예외 처리 및 에러 메시지 관리
- @ControllerAdvice + @ExceptionHandler를 활용하여 전역 예외 처리
- 예외 메시지는 Enum으로 관리
- 코드 스타일
- Formatter 사용하여 일관된 띄어쓰기 적용
- import * 규칙 -> 7개 이상은 *
2. 구현 기능 - 내가 맡은 부분
-워크스페이스
- 워크스페이스 생성, 수정, 삭제, 조회 기능
- 관리자가 새로운 워크스페이스를 생성할 수 있음
- 워크스페이스 정보(이름, 설명) 수정 가능
- 워크스페이스 삭제 가능(관련 데이터 일괄 삭제)
- 워크스페이스 조회 최적화
- 워크스페이스 멤버 초대 기능(이메일 기반 초대)
- 특정 이메일을 입력하여 해당 유저를 초대할 수 있음
- 초대 가능한 역할(관리자, 멤버, 읽기 전용) 설정 가능
- 워크스페이스 내 권한 관리
- 관리자는 모든 기능 수행 가능
- 멤버는 보드 생성 가능, 일부 기능 제한
- 읽기 전용 사용자는 조회만 가능
- 적용 기술 및 개념
- JPA 쿼리 최적화
- JOIN FETCH를 활용하여 N+1 문제 해결
- @Query("SELECT m FROM Member m JOIN FETCH m.workspace WHERE m.user = :user")
- JPA 쿼리 최적화
-리스트
- 리스트 생성, 수정, 삭제 기능
- 보드 내에서 새로운 리스트를 추가할 수 있음
- 리스트의 제목을 수정할 수 있음
- 리스트 삭제 시, 해당 리스트 내 카드도 함께 삭제됨
- 리스트 순서 정렬(GreenHopper 알고리즘 적용)
- 리스트의 순서는 BigDecimal을 활용한 GreenHopper 알고리즘으로 정렬
- 리스트 이동 시 기존 리스트의 순서값을 기준으로 새로운 중간값을 자동 생성하여 정렬
- 적용 기술 및 개념
- GreenHopper 알고리즘 적용
- BigDecimal 기반의 리스트 정렬 방식
- 리스트를 특정 위치로 이동할 때 기존 값과 새로운 값의 중간값을 자동 생성하여 정렬
- 기존 정수 기반 정렬 방식의 한계를 해결하고, 리스트 간격 유지 및 정렬 충돌 방지
- 트랜잭션 관리 적용
- 리스트 생성, 수정, 삭제 시 데이터 정합성 유지를 위해 트랜잭션 적용
- GreenHopper 알고리즘 적용
-첨부파일
- 파일 업로드 및 삭제 기능(AWS S3 사용)
- 카드에 파일 업로드 가능
- 사용자는 자신이 업로드한 파일만 삭제할 수 있음
- 파일 검증(크기 및 확장자 제한)
- 허용 확장자 : jpg, png, pdf, csv
- 최대 5MB 파일만 업로드 가능
- 적용 기술 및 개념
- MultipartFile : 업로드된 파일의 유효성 검사(getSize(), getOriginalFilename())
- AWS S3 SDK(software.amazon.awssdk.services.s3.S3Client)
- S3Client.putObject()를 활용한 파일 업로드 처리
- S3Client.deleteObject()를 통한 파일 삭제 구현
- 파일 검증 로직
- 5MB 이상의 파일 업로드 시 BAD_REQUEST 예외 발생
- 지원되지 않는 확장자(jpg, png, pdf, csv 외) 업로드 시 BAD_REQUEST 예외 발생
- Spring Security 기반 권한 제어
- authentication.getName() 을 활용하여 사용자가 자신의 파일만 삭제할 수 있도록 제한
3. 팀원 구현 기능 분석
-회원가입/로그인
- 회원가입 : Bcrypt를 사용하여 비밀번호를 해싱하여 저장
- 로그인 : 로그인 성공 시 JWT AccessToken, RefreshToken 발급
- JWT 인증 관리
- 모든 API 요청에 대해 JWT AccessToken 검증 후 인증 처리
- AccessToken이 만료되면 RefreshToken을 이용해 재발급
- RefreshToken을 Redis에 저장하여 인증 상태 유지
- 적용 기술 및 개념
- Spring Security & JWT 인증
- JwtProvider 클래스를 이용해 JWT 생성 및 검증
- Authentication 객체를 활용하여 인증 정보 관리
- Bcrypt 비밀번호 암호화 : PasswordEncoder를 사용하여 암호화 후 저장
- Redis 기반 토큰 관리
- RefreshToken을 Redis에 저장하여 인증 유지
- StringRedisTemplate을 활용하여 Redis에서 RefreshToken 조회 및 삭제
- Spring Security & JWT 인증
-카드(LexoRank 적용)
- 카드 정렬(LexoRank 적용)
- 리스트 내에서 카드의 순서를 변경할 수 있음
- LexoRank 알고리즘을 활용하여 정렬 유지
- 중간 삽입 시 기존 순서값을 고려하여 새로운 순서값 계산
- 적용 기술 및 개념
- LexoRank 기반 카드 정렬
- 리스트 내에서 카드 위치를 정밀한 순서 값으로 정렬 유지
- LexoRank.getMiddleRank(before, after)를 이용해 카드 정렬
- LexoRank 기반 카드 정렬
-알림(Slack 연동)
- 멤버 추가, 카드 변경, 댓글 작성 등 주요 이벤트에 대한 실시간 알림 제공
- 알림 저장 로직
- Notify 엔티티를 생성하여 데이터베이스에 알림 내역 저장
- created_at 필드를 활용하여 알림 발생 시간을 기록
- 특정 워크스페이스와 연관된 알림을 관리
- AOP(Aspect-Oriented Programmin) 활용
- @AfterReturning 어노테이션을 사용하여 특정 컨트롤러의 메서드가 실행된 후 알림을 자동으로 전송
- WorkspaceController, BoardController, CardController, ListController, CommentController의 모든 메서드에 적용
- Slack API 연동
- callSlackApi() 메서드를 사용하여 Slack의 chat.postMessage API 호출
- 요청 시 워크스페이스의 notifyToken과 notifyChannel을 활용하여 알림 전송
- 각 엔티티의 변경 사항을 감지하여 Slack 메시지 자동 전송
- 적용 기술 및 개념
- AOP 기반 이벤트 감지(@Aspect, @AfterReturning)
- 특정 컨트롤러의 실행 결과를 감지하여 알림을 자동 실행
- Slack API 호출(RestTemplate)
- HttpHeaders를 사용하여 Authorization 헤더 설정
- restTemplate.exchange()를 활용하여 POST 요청 전송
- AOP 기반 이벤트 감지(@Aspect, @AfterReturning)
-최적화
- 카드 검색 성능 최적화
- title, description 컬럼에 인덱스 추가
- 검색 쿼리(JPQL) 최적화를 통해 N+1 문제 해결
- 대량 데이터 성능 테스트
- PostConstruct를 활용하여 10만개의 카드 데이터 삽입 및 검색 쿼리 실행시간 측정 및 성능 비교
- 인덱싱 vs 쿼리 최적화 테스트
- Test1 : 인덱싱 적용 전/후 조회 속도 비교
- Test2 : JOIN FETCH를 활용한 쿼리 최적화 적용
- Test3 : 인덱싱 + 쿼리 최적화 적용
- 최적화 적용 우선순위 결정
- 쿼리가 무거운 경우 -> 인덱싱 효과가 크다
- 쿼리가 상대적으로 가벼운 경우 -> 쿼리 최적화가 더 중요
- 최적화 순서 : 쿼리 최적화를 우선 적용 후, 필요하면 인덱싱으로 보완
- 적용 기술 및 개념
- 데이터베이스 인덱싱
- 단일 인덱스 : idx_title, idx_description
- 복합 인덱스 : idx_title_description
- 검색 시 불필요한 Full Table Scan 방지
- JPQL 최적화 : JOIN FETCH를 활용해 연관 테이블을 한 번의 쿼리로 가져와 N+1 문제 해결
- 성능 테스트 : PostConstruct로 대량 데이터 삽입, 랜덤 데이터 생성 후 실제 환경과 유사한 조건에서 검색 테스트 수행
- 쿼리 실행시간 로깅(AOP 활용)
- 검색 실행 시간을 자동으로 측정하여 성능 분석
- 실행 시간을 자동으로 측정하여 느린 쿼리를 분석하고 최적화 가능
- 데이터베이스 인덱싱
-동시성 제어
- 리스트 및 카드의 동시 수정 방지
- 여러 사용자가 동시에 리스트 순서를 변경할 경우 충돌 방지
- Redis 기반 분산 락을 활용하여 순서 재정렬 시 충돌 방지
- 리스트 정렬 시 락 적용
- updateList() 메서드에 Redis SETNX(분산 락) 사용
- LOCK_EXPIRATION_TIME을 설정하여 일정 시간 이후 락 자동 해제
- 적용 기술 및 개념
- Redis 기반 분산 락 활용(SETNX)
- StringRedisTemplate을 활용하여 특정 리스트 ID에 대한 락 설정
- setIfAbsent()을 활용하여 동시 수정 방지
- 트랜잭션 관리(@Transactional)
- 리스트 업데이트 시 트랜잭션을 활용하여 정합성 유지
- Redis 기반 분산 락 활용(SETNX)
-캐싱
- 카드 상세 조회 시, 조회수를 Redis에 캐싱하여 데이터베이스 부하 감소
- Redis Sorted Set(ZSet)을 활용하여 조회수 기반 카드 랭킹 시스템 구축
- 조회수 중복 방지 : 동일 유저가 하루에 한 번만 증가하도록 TTL 설정
- 매일 자정 Redis 캐시 초기화(Spring Scheduler 적용)
- 적용 기술 및 개념
- Redis(String, ZSet 활용)
- String -> 카드별 조회수 저장
- ZSet -> 조회수 기반으로 인기 카드 순위 저장 및 조회
- Spring Scheduler(@Scheduled)
- 매일 자정 조회수 캐시 자동 초기화
- 동시성 제어
- StringRedisTemplate을 이용해 조회수 중복 증가 방지
- TTL(1일) 설정을 통해 특정 유저가 조회수를 무한 증가시키지 못하도록 제한
- Redis(String, ZSet 활용)
-배포(Docker + AWS 기반 CI/CD 파이프라인)
- GitHub Actions를 이용하여 CI/CD 자동화 파이프라인 구축
- Docker를 활용한 컨테이너화 및 배포 자동화
- AWS EC2에 Docker Compose를 사용하여 애플리케이션 배포
- AWS RDS(MySQL) 분리 운영 : 데이터베이스와 애플리케이션 서버 분리
- 적용 기술 및 개념
- CI/CD 파이프라인 구축
- GitHub에 코드를 push 하면 GitHub Actions가 동작하여 CI/CD 실행
- CI : 코드 빌드 및 테스트 자동화
- CD : Docker 이미지 빌드 -> Docker Hub에 push -> EC2에서 자동 배포
- Docker 기반 컨테이너화
- Docker를 활용하여 Spring Boot 애플리케이션 컨테이너화
- Docker Compose를 사용하여 app, db, redis 컨테이너 관리
- AWS EC2 배포
- EC2에 접속하여 docker-compose를 실행하여 애플리케이션 배포
- Docker Hub에서 최신 이미지를 pull 한 후 재시작
- GitHub Actions에서 자동으로 최신 이미지를 배포하는 스크립트 실행
- AWS RDS(MySQL) 분리 운영
- 데이터베이스를 AWS RDS(MySQL)로 분리하여 EC2와 독립적으로 운영, 안정성 증가
- CI/CD 파이프라인 구축
진행 중 어려웠던 점과 해결방법
& 피드백
1. 진행 중 문제점과 해결방법은 트러블 슈팅을 작성해 정리
https://jy3574.tistory.com/130
[트러블 슈팅] Trello(칸반보드) 프로젝트
1. GreenHopper 알고리즘 정밀도 문제1)개요리스트 정렬을 위해 GreenHopper 알고리즘을 사용하여 position 값을 생성하였는데, 처음엔 int 자료형을 사용하여 중간값을 계산했지만, 정밀도 문제로 인해 예
jy3574.tistory.com
2. 설계에 관한 피드백
- 카드 검색 API에서 boardId가 누락됨 -> 파라미터로 추가
- 비밀번호 확인 API의 URL은 RESTful 규약을 따르기 위해 users/check-password로 변경
- 알림 테이블은 따로 만들어서 관리(workspace 별로 알림을 발송하도록 workspaceID 를 외래키로 가지고 있는채로)
3. 코드 구현 후 최종 피드백
- 리스트 생성 시 Position 최적화
- findByBoardId() 대신 countByBoardId()를 활용하여 리스트 개수 +1을 position 값으로 설정하면 성능 최적화 가능
- 불필요한 MAX 연산을 줄여 DB 성능 최적화
- User 정보 조회 방식 개선
- 현재 : findByEmail()을 사용하여 서비스 레이어에서 매번 DB를 조회하여 User 정보를 가져옴
- Spring Security 적용 시 @AuthenticationPrincipal UserDetailsImpl userDetailsImpl을 사용하여 SecurityContextHolder에서 직접 User 정보를 가져오기
- 서비스 레이어에서 추가적인 DB 조회 없이 유저 정보를 빠르게 활용 가능
- 불필요한 API 제거
- 카드, 유저, 보드 등에서 이미지를 조회할 때 해당 이미지의 URL만 응답하면 프론트엔드에서 자동으로 이미지가 로드됨
- 파일을 조회하는 API를 제거하고, 각 엔티티의 응답 DTO에서 imageUrl을 포함하는 방식으로 개선
- InterceptorUtils 내 getTokenFromRequest() 개선
- Spring Security가 적용된 경우, SecurityContextHolder에서 직접 User 정보를 가져올 수 있음
- S3 파일 정리 로직 추가
- 카드 또는 유저 프로필 삭제 시, S3에서 해당 파일도 함께 삭제하도록 로직 추가
- AWS SDK의 deleteObject()를 활용하여 S3에서 불필요한 파일 제거
- 분산락 개선
- 현재 : Redis를 사용한 분산락을 적용하여 동시성 제어를 하고 있지만, 락 해제와 트랜잭션 종료 사이의 짧은 시간 차이 동안 다른 쓰레드가 락을 점유할 가능성이 있음
- 트랜잭션 종료 후에 락을 해제하는 방식으로 코드 수정
느낀점 및 개선해야할 점
작성 중...
적용한 알고리즘
https://jy3574.tistory.com/131
[알고리즘] LexoRank 알고리즘
LexoRank 알고리즘 개념LexoRank는 동적인 정렬을 효율적으로 유지하기 위해 설계된 알고리즘으로, 연속적인 데이터의 순서를 유지하면서도 중간 삽입이 가능하도록 설계된 정렬 방식 Trello와 같은
jy3574.tistory.com
'TIL(Today I Learned) > 프로젝트' 카테고리의 다른 글
[TIL] 최종 프로젝트 : 9kcal. 오늘 뭐 먹지? (2~3 주차. 구현 시작 및 중간 발표 준비) (0) | 2025.03.04 |
---|---|
[TIL] 최종 프로젝트 : 9kcal. 오늘 뭐 먹지? (1~2 주차. 프로젝트 시작 및 설계 과정) (0) | 2025.02.25 |
[TIL] 배달 앱 아웃소싱 프로젝트 (0) | 2024.12.19 |
[TIL] 고객과 통화 연관관계로 환전요청 만들기 (0) | 2024.12.02 |
[TIL] 뉴스피드 프로젝트 (1) | 2024.11.26 |