대학교 홈페이지에 공지사항이 올라오면 자동으로 푸시 알림을 전송하는 서비스입니다.
서비스 종료전까지의 누적 사용자는 약 1,000명 입니다.
- Languages & Frameworks: Kotlin 1.9.25, Spring Boot 3.3.4
- Database: MySQL 8.0.35, MongoDB 7.0.15
- Infrastructure: AWS (EC2, RDS, SQS, Lambda), Docker
- CI/CD & Monitoring: Jenkins, GitHub Actions, Prometheus, Grafana
- Testing & Quality: JUnit5, JaCoCo, Codecov
- Release 1.0.0 (2024.01 ~ 2024.04) - 개발 및 출시
- Release 1.0.1 (2024.05) - 버그 수정
- Release 1.1.0 (2024.07) - 서비스 개선
- Release 1.1.1 (2024.09) - 버그 수정
- Release 1.2.0 (2024.10 ~ 2024.11) - 아키텍처 개선
- Release 1.2.1 (2025.02 ~ 2025.03) - 학과명 변경 및 신설 학과 대응
- Release 1.2.2 (2025.03) - 학교 홈페이지 리뉴얼 대응
Release 1.0.0 ~ 1.1.1 소스 코드는 GitHub 프로젝트에서 확인 가능합니다.
- 대학교 홈페이지 스크래핑 로직 개발 (공지사항, 학사일정, 식단표)
- 공지사항, 학사일정, 식단표 조회 API 개발
- CI/CD 파이프라인 구축
- Java에서 Kotlin으로 마이그레이션 및 리팩토링
- API 리팩토링 (Restful API 적용)
- 알림 설정 API 성능 99% 개선
- 단일 모듈에서 멀티 모듈을 적용하여 아키텍처를 변경
- API 서버와 Admin 서버 분리
- 세컨더리 인덱스를 추가하여, 쿼리 성능 최적화
- 서버 앞으로의 개선 방향
- Kotlin + Spring / Multi-Module 적용
- Jnuit5, Mocktito 테스트 코드 작성
- 테스트 커버리지 관리 및 테스트 자동화
- Jenkins CI / CD 구축
- Prometheus + Grafana 모니터링 시스템 구축
- FCM 메시지 전송에 SQS 활용
- Release 1.2.0 배포 완료
- MySQL 세컨더리 인덱스를 활용한 성능 개선
기술 스택: Java 17, Spring Boot, Spring JPA, MySQL, Redis, FCM, AWS(EC2, RDS)
- Jsoup 라이브러리를 활용하여 정적 스크래핑 로직을 개발
- 정적 스크래핑을 선택한 이유
- 비용 효율성: 동적 스크래핑은 브라우저 렌더링을 필요로 하여 리소스와 실행 속도에 부담이 큼 반면, 정적 스크래핑은 HTML 소스를 직접 파싱하여 빠르고 서버 부담이 적음
- 데이터 특성: 서버에서 제공하는 정적 HTML로 충분히 처리 가능하여 동적 스크래핑의 필요성 없음
원인: Redis에 사용자의 푸시 알림 토큰을 저장했으나 TTL 설정이 되어 있었고, 토큰 갱신 API가 없었던 상황으로 토큰이 만료됨
해결 방법:
- Redis TTL을 1개월로 설정한 것을 확인하고, 토큰의 TTL을 해제하여 문제 해결
- 출시 직후라 문제는 개발자 토큰에서만 발생했으며, 사용자 토큰에는 영향을 미치지 않음
개선 사항: FCM 푸시 알림 관련 동료 간 커뮤니케이션 부족으로 발생한 문제였음 이를 해결하기 위해 회의 내용을 문서화하여 향후 커뮤니케이션 문제를 방지함
기술 스택: 1.0.0 버전과 동일
기존의 [POST] /department/v1/dmu/updateDepartment에서 [PUT] /api/v1/subscribe/department와 같은 RESTful한 방식으로 리팩토링하여 멱등성과 통일성을 보장
문제 상황
- 기존 FCM Topic API를 사용하여 각 Topic에 토큰을 저장했지만, 1초의 응답 시간이 걸리는 문제를 확인
- 알림 설정을 위한 20개의 키워드에 대해 최대 20초가 소요되었음
해결 방법
- FCM Topic API를 사용하지 않고, MySQL에서 알림 관련 정보(Token, 학과, 키워드 등)를 관리하도록 변경
- FCM Topic API를 비동기로 처리하는 방법도 고려했지만, 다음과 같은 상황을 고려함
- 사용자가 반복적으로 알림을 껐다 켰다 할 경우, 반복하면 단기간에 너무 많은 API를 호출이 발생할 수 있는 문제가 있음
결론
- 알림 설정 API 응답 시간을 127ms로 개선
- 푸시 알림 설정 정보를 서버에서 관리함으로써 확장성(특정 사용자 또는 전체 사용자 푸시 알림 전송)을 고려한, 유연한 환경으로 전환됨
원인: 공지사항을 스크랩핑 하는 로직은 오전 10시와 오후 5시 총 2번 작동하게 되어있었는데, 퇴근 시간 이후에 공지사항을 업로드 하는 종종 일이 발생
해결방법: 스크래핑 로직을 6시에 동작하도록 변경
하지만, 이는 근본적인 해결책이 아니라고 판단하였고, 추후 Release 1.2.0에서 10분 단위로 오전 9시부터 오후 7시까지 주기적으로 스크래핑하도록 변경함
기술 스택: Kotlin, Spring Boot, Spring JPA, MySQL, MongoDB, Docker, Jenkins, FCM, AWS (EC2, RDS, SQS, Lambda), Prometheus, Grafana
- Null 안전성과 코드 가독성을 위해 Java에서 Kotlin으로 리팩토링
- 개인적으로 Kotlin을 주 언어로 사용하기 위한 학습 목적도 있었음
- 클린 아키텍처를 기반으로 Presentation, Domain, Infrastructure 모듈로 분리하여 확장성과 유지보수성을 향상
- 스크래핑 로직도 독립적인 모듈로 분리하여 각 계층 간 의존성을 최소화
- Spring Batch 대신, 주기적인 스크래핑과 푸시 알림 전송 로직을 Admin 서버로 분리하여 관리
- 트래픽 증가 시 스케일 아웃을 고려한 분리
- SQS와 Lambda를 사용하여 푸시 알림 부하를 분산시키고, FCM 의존성 제거
- Jenkins를 이용하여 CI/CD 파이프라인을 구축하여 자동화된 배포 프로세스 구현
- 세컨더리 인덱스와 쿼리 성능 최적화 작업을 통해 시스템의 응답 속도 개선
- 구 버전 애플리케이션을 사용하고 있는 사용자를 가정하였음
- 애플리케이션 버전에 상관없이 API를 제공하기 위해 학과명을 핸들링 하도록 수정함
- 추후, 신 버전으로 전부 이전 될 것이기 때문에, 구 학과명을 새로운 학과명으로 변경하는 유틸 클래스를 추가
- 신설 학과 공지사항의 스크래핑 로직을 추가 작성
- CSS만 변경될 것으로 예상되었지만, HTML 구조도 전부 변경되어서 스크래핑 로직에서 에러가 발생한다는 사실을 모니터링을 통해서 확인
- 변경된 학교 홈페이지 HTML구조를 분석하고, 스크래핑 로직을 일괄 수정
- 인덱싱을 위해 date, id로 정렬되던 페이지네이션의 기준을 number로 수정
- 이를 통해 기존 조회 속도 대비 55% 향상