리팩토링 및 CI/CD 최적화
1. 개요
지난 2주 동안, 시스템 리팩토링과 CI/CD(Continuous Integration/Continuous Deployment) 최적화 작업에 중점을 두고 다양한 기술과 방법을 학습하고 적용하였습니다. 주요 학습 내용은 시스템 성능 향상, 코드 유지보수성 개선, 이벤트 기반 아키텍처 도입, 데이터 무결성 보장, 그리고 CI/CD 파이프라인 구축을 포함합니다.
2. 주요 리팩토링 작업
2.1 ReserveRequest
리팩토링
배경: 결제 생성 요청 로직이 Reservation
엔티티에 포함되어 있어 단일 책임 원칙(SRP)에 위배되었습니다.
변경 내용: 결제 생성 요청 로직을 Reservation
에서 ReserveRequest
로 이동하였습니다.
기존: 결제 생성 로직이
Reservation
엔티티에 포함public Reservation toEntity(ConcertReader concertReader, UserReader userReader) { Concert concert = concertReader.findConcert(concertId); ConcertDate concertDate = concertReader.findConcertDate(concertDateId); Seat seat = concertReader.findSeat(seatId); User user = userReader.findUser(userId); return Reservation.builder() .concert(concert) .concertDate(concertDate) .seat(seat) .user(user) .status(Reservation.Status.ING) .build(); }
변경 후: 결제 생성 로직을
ReserveRequest
로 분리public Reservation toEntity() { return Reservation.builder() .concertId(concertId) .concertDateId(concertDateId) .seatId(seatId) .userId(userId) .status(Reservation.Status.ING) .build(); } public CreatePaymentReqDto toCreatePayment(Reservation reservation) { return new CreatePaymentReqDto(reservation, Payment.Status.READY, reservation.getSeat().getPrice()); }
결과:
- 단일 책임 원칙을 준수하여 각 클래스가 자신의 역할에 집중할 수 있게 되었습니다.
- 코드의 유지보수성과 가독성이 향상되었습니다.
2.2 ReservationEventListener
추가
배경: 예약 점유 이벤트를 처리하기 위한 비동기 이벤트 리스너가 필요하였습니다.
변경 내용: 예약 점유 이벤트를 처리하기 위해 이벤트 리스너를 추가하였습니다.
신규 추가:
@Component @RequiredArgsConstructor public class ReservationEventListener { private final ReservationMonitor reservationMonitor; @EventListener @Transactional(propagation = Propagation.REQUIRES_NEW) public void onReservationOccupiedEvent(ReservationOccupiedEvent event) { reservationMonitor.occupyReservation(event.getReservationId()); } }
결과:
- 이벤트 기반 아키텍처를 통해 비동기적으로 예약 점유 상태를 관리할 수 있게 되었습니다.
- 시스템의 모듈성과 확장성이 높아졌습니다.
@Transactional(propagation = Propagation.REQUIRES_NEW)
를 사용하여 새로운 트랜잭션에서 이벤트를 처리함으로써, 기존 트랜잭션과의 충돌을 방지하였습니다.
2.3 ReservationOccupiedEvent
추가
배경: 예약 점유 이벤트를 정의하여 이벤트 기반 아키텍처를 지원할 필요가 있었습니다.
변경 내용: 예약 점유 이벤트를 정의하였습니다.
신규 추가:
@Getter public class ReservationOccupiedEvent extends ApplicationEvent { private final Long reservationId; public ReservationOccupiedEvent(Object source, Long reservationId) { super(source); this.reservationId = reservationId; } }
결과:
- 시스템의 모듈성과 확장성이 높아졌습니다.
- 이벤트 기반 아키텍처를 통해 비동기적으로 예약 점유 상태를 관리할 수 있게 되었습니다.
2.4 WaitingQueue
상태 관리 추가
배경: 대기열의 상태를 명확히 관리할 필요가 있었습니다.
변경 내용: WaitingQueue
클래스에 상태를 관리하는 status
필드를 추가하였습니다.
- 기존: 상태 관리 필드가 없음
- 변경 후:
@Column(nullable = false) @Enumerated(EnumType.STRING) private Status status;
결과:
- 대기열 상태 관리가 명확해져 시스템의 안정성과 데이터 일관성이 향상되었습니다.
2.5 ReservationService
예외 처리 변경
배경: 예약 중복 시 발생하는 예외를 더 적합한 예외로 변경할 필요가 있었습니다.
변경 내용: ObjectOptimisticLockingFailureException
에서 DataIntegrityViolationException
으로 예외를 변경하였습니다.
- 기존:
} catch (ObjectOptimisticLockingFailureException e) { // 버전 충돌 -> "이미 선택된 좌석입니다." 반환
- 변경 후:
} catch (DataIntegrityViolationException e) { // 유니크 제약 조건(concertDateId, seatId) 위반 시 throw new CustomException(ReservationExceptionEnum.ALREADY_RESERVED, null, LogLevel.INFO);
결과:
- 더 적절한 예외 처리를 통해 중복 예약 시도를 효과적으로 관리할 수 있게 되었습니다.
3. CI/CD 파이프라인 구축
3.1 GitHub Actions 설정
배경: 효율적인 CI/CD 파이프라인을 구축하여 코드 변경이 자동으로 빌드, 테스트, 배포되도록 설정할 필요가 있었습니다.
변경 내용: GitHub Actions를 사용하여 CI/CD 파이프라인을 설정하였습니다.
3.2 CI/CD 파이프라인 세부 구현
CI develop Pipeline:
- 개발자가
develop
또는release-*
브랜치에 푸시하거나 PR을 생성하면, GitHub Actions가CI develop Pipeline
을 시작합니다. - 주요 단계:
- Redis, Zookeeper, Kafka 서비스 시작
- 코드 체크아웃 및 JDK 설정
- 테스트 실행 및 리포트 생성
- Checkstyle 실행
- 프로젝트 빌드
name: CI develop Pipeline
on:
push:
branches:
- develop
- release-*
pull_request:
branches:
- develop
- release-*
jobs:
build:
runs-on: ubuntu-latest
services:
redis:
image: redis
ports:
- 6379:6379
zookeeper:
image: wurstmeister/zookeeper
ports:
- 2181:2181
kafka:
image: wurstmeister/kafka
ports:
- 9092:9092
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
- name: Build with Gradle
run: ./gradlew build
- name: Run tests
run: ./gradlew test
- name: Generate report
run: ./gradlew jacocoTestReport
- name: Run Checkstyle
run: ./gradlew checkstyleMain
Dev CD Pipeline:
- 개발자가
release-*
브랜치에 푸시하면 GitHub Actions가Dev CD Pipeline
을 시작합니다. - 주요 단계:
- AWS 자격 증명 설정
- Docker 이미지 빌드 및 ECR에 푸시
name: Dev CD Pipeline
on:
push:
branches:
- release-*
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Log in to Amazon ECR
run: |
aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com
- name: Build and push Docker image
run: |
docker build -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/my-app:${{ github.sha }} .
docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/my-app:${{ github.sha }}
- name: Deploy to ECS
run: |
aws ecs update-service --cluster my-cluster --service my-service --force-new-deployment
Prod CD Pipeline:
- **개발자가
master
브랜치에 푸시하면 GitHub Actions가Prod CD Pipeline
을 시작합니다
.**
- 주요 단계:
- AWS 자격 증명 설정
- Docker 이미지 빌드 및 ECR에 푸시
- ECS 태스크 정의 배포
name: Prod CD Pipeline
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Log in to Amazon ECR
run: |
aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com
- name: Build and push Docker image
run: |
docker build -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/my-app:${{ github.sha }} .
docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/my-app:${{ github.sha }}
- name: Deploy to ECS
run: |
aws ecs update-service --cluster my-cluster --service my-service --force-new-deployment
4. CI/CD 파이프라인의 이점
4.1 자동화된 테스트 및 배포
- 이점: 코드 변경이 자동으로 빌드, 테스트, 배포되므로 개발 주기가 단축되고, 인적 오류가 줄어듭니다.
4.2 일관된 배포
- 이점: 동일한 환경에서 동일한 절차로 배포가 이루어져 환경 간 일관성이 보장됩니다.
4.3 빠른 피드백 루프
- 이점: 코드 변경 시 즉각적인 피드백을 받을 수 있어 문제를 빠르게 식별하고 해결할 수 있습니다.
4.4 향상된 협업
- 이점: 팀원 간의 코드 변경 사항이 자동으로 통합되고 테스트되므로 협업이 원활해집니다.
5. 결론
지난 2주 동안의 학습과 실습을 통해 시스템 리팩토링과 CI/CD 최적화 작업을 성공적으로 수행할 수 있었습니다. 이를 통해 시스템의 성능과 유지보수성이 크게 향상되었으며, 자동화된 CI/CD 파이프라인을 통해 개발 효율성과 신뢰성을 높일 수 있었습니다. 이러한 개선 작업은 시스템의 장기적인 안정성과 확장성에 기여할 것입니다.
'스케쥴 > 스터디' 카테고리의 다른 글
[항해99 취업리부트 WIL] 8주차 (0) | 2024.07.16 |
---|---|
[항해99 취업리부트 WIL] 7주차 (0) | 2024.07.09 |
[항해99 취업리부트 WIL] 5주차 (0) | 2024.06.24 |
[항해99 취업리부트 TIL] 4주차 4일 (1) | 2024.06.17 |
[항해99 취업리부트 TIL] 4주차 3일 (0) | 2024.06.15 |