Project/Newsfeed

[프로젝트 회고] 뉴스피드 개인 프로젝트를 마치며

쉬지마 이굥진 2024. 4. 25. 23:12

약 6주간의 시간 동안 진행했던 내 첫 번째 개인 프로젝트가 드디어 끝났다.

 

100% 내가 해야하는 모든 기술적 의사결정과 처음 써 보는 스택들로 설렘 반 막막함 반이었던 개인 플젝이였다. 개발이 잘 풀렸던 적도 있었고 생각했던 대로 안 풀렸던 적도 있어서 힘들 때도 있었지만, 같은 주제로 프로젝트를 진행했던 동료들이 있어서 도움을 주고 도움을 받으며 중간에 포기하지 않고 무사히 끝낼 수 있었다고 생각한다.

 

이 타이밍에 프로젝트를 하면서 느꼈던 점들을 정리하고 되돌아보면서, 다음 프로젝트 땐 어떻게 더 발전할 수 있을지 회고해보는 시간을 가져보려 한다.

 

📌목적

  • 기여도 100%의 개인 프로젝트를 진행해보면서, 소프트웨어 설계부터 개발, 테스트까지 전반적인 진행 과정을 모두 경험해보는 것
  • 직전에 진행했던 팀 프로젝트에서 기능적으로 아쉬웠던 부분들을 보완하고 싶었기에, 써보지 않았던 스택/기술들을 적용해보는 것

 

📌기술적 의사결정

Java 17
LTS 버전으로 최신 기능과 안정성을 제공한다. gc 처리량, 지연시간면에서 이전 버전에 비해 성능이 향상됐고,  지원 기간이 길기 때문에 장기적인 안정성과 보안 패치를 보장받을 수 있어 장기간 서비스 운영에 유리하다.

또한 Spring boot 3.0부터는 17 이상 지원하기 때문에 호환성 측면에서 선택했다.

 

Spring Boot
전에 팀 프로젝트를 할 때는 스프링 프레임워크를 사용했었다.  XML 설정파일, Java config등 구성을 정의해야 하지만 부트는 이러한 것들이 내장되어 있기 때문에 설정할 필요가 없고, 무엇보다 apache tomcat이 내장되어 있어 서버를 따로 설정해 주지 않아도 되어 선택했다.
(실제로 매우매우 편했고 개발 초기 설정에 소요되는 시간을 크게 단축할 수 있었다.)

 

JPA
전 팀 프로젝트를 할 때는 MyBatis 를 통해 JDBC와 연동해서 SQL문을 Mapper 클래스에서 따로 작성 해야만 했다.
반복적이고 간단한 SQL문 작성 시간을 줄이면, 개발에 집중할 수 있고 유지보수가 쉬울 것이라고 생각했기 때문에 JPA를 선택했다.

 

MySQL
먼저, 테이블 간 관계 설정이 필요해서 관계형 데이터 베이스를 선택했다.
이전 팀프로젝트에서는 Oracle을 사용했었는데, 그 때 id 컬럼 자동증가 (MySQL에서는 auto increment 기능) 기능이 없어서 따로 그에 관한 처리를 해줘야 했다. 그래서 이번엔 auto increment 기능이 있는 MySQL을 선택했다.

(진짜 짱)

 

JWT
전에 했던 프로젝트에서는 회원 관련 로직을 세션 인증 방식으로 했었는데, 세션 인증 방식에 비해 서버 부담이 적은 토큰 인증 방식으로 진행해보고 싶어 선택하게 되었다.

 

QueryDSL
검색 API를 구현하면서 선택하게 됐는데, 기본적으로 JPA 방식은 JPA가 메서드명을 자동으로 인식해서 쿼리문을 작성하기도 하지만, 검색과 같이 특정 조건이 있고 복잡한 쿼리문일 경우 직접 작성해야 하는 경우가 있다. 이럴 때 오타나 문법 오류를 찾기 어렵다는 단점이 있었다.

QueryDSL은 JPA와 함께 사용하며 복잡한 동적 쿼리를 유연하게 작성할 수 있고, 컴파일 시점에 오류를 검출해주며 뉴스피드의 다양한 검색 조회 조건을 효율적으로 처리할 수 있었기 때문에 선택했다.

 

AWS S3
뉴스피드 프로젝트는 사용자가 생성하는 멀티미디어(이미지, 동영상 등)가 특히 많은 케이스이다.
그렇기에 거의 무한대에 가까운 스토리지 용량을 제공하는 S3가 적합하다고 생각했고, 데이터가 증가해도 추가 하드웨어나 스토리지 관리에 대한 걱정 없이 확장할 수 있다는 좋은 확장성과 내구성, 가용성 때문에 선택했다.

 

📌프로젝트 소개

◾Java, Spring boot 기반 REST API 방식
◾보안 및 인증 - JWT, Spring Security / 그 외 QueryDSL, AWS S3 등 사용
◾Artillery 부하테스트를 이용하여 성능 측정 및 개선

 

매일 매일 찍는 반려동물 사진을 일반 사람들 눈치보지 않고 내 피드에 올려 자랑할 수 있는, 반려인을 위한 뉴스피드를 만들고 싶었다 😀

뉴스피드에 맞게 회원가입/로그인 기능, 관리자 기능, 팔로우/팔로워 기능, 좋아요 기능, 뉴스피드와 댓글/대댓글 기능과 멀티미디어를 위한 AWS S3 이용 등의 기능을 구현했다.

ERD

  • JPA를 처음 학습해보면서 ERD 쪽에서도 외래키 참조 관계를 정확히 학습한 후 적용했다.
  • soft delete에 대한 개념을 알게 되면서, 유저 도메인 hard delete 👉 sofe delete 적용을 위해 기본값이 Y인 status 컬럼을 추가하여 탈퇴 시 값을 N으로 변경되도록 구현했다.
  • 비밀번호 수정 기능에선, 이전 3번동안 설정했던 비밀번호로는 수정할 수 없도록 요구사항을 정의했다. 이 부분을 어떻게 구현할까 고민하면서는 password_history 테이블을 따로 만들어서 비밀번호 이력을 저장하여 비교하는 식으로 구현했다.

 

API 명세서

  • 초반 작업은 노션으로 진행
  • 이후 깃허브 레포에서 리드미를 쓸 때는 Postman의 documentation 기능을 활용해서 API 명세를 작성했다.
    • body 응답과 각종 예외 상황에 맞게 반환하는 에러 메세지까지 응답 예시에 포함하여 꼼꼼히 작성했다! 

 

구현한 기능

  • 유저 - 회원가입 / 로그인 / 카카오 로그인 / 프로필 이미지(AWS S3) / 프로필 수정
  • 관리자 - 권한 부여 /  강제 탈퇴 / 게시글과 댓글 수정 및 삭제 / 탈퇴한 유저까지 포함한 모든 유저 조회
  • 게시글 - QueryDSL 이용한 검색 기능/ 삭제 시 댓글, 좋아요도 함께 삭제 되도록 casecade 적용, 멀티미디어는 AWS S3 이용하여 업로드
  • 댓글/대댓글 - 삭제 시 좋아요 함께 삭제 되도록 casecade 적용
  • 좋아요 - 토글 형식으로 구현
  • 팔로우 - 내가 팔로우 한 유저들의 게시글 목록 조회, 토글 형식으로 구현

 

코드 리팩토링

  • 예외처리 Custom 및 공통화
    • RuntimeException을 상속받아, 실행 예외에 ErrorCode 필드를 추가해 커스텀
    • @RestControllerAdvice @ExceptionHandler 이용해 전역에서 발생하는 예외 핸들링
  • Spring AOP로 데이터 로깅, 수행시간 측정
  • Service 비즈니스 로직에서 사용자 확인, 기존 게시글 확인 등 반복되는 조건문은 따로 분리하여 객체지향적 설계
  • 게시글 목록 조회 API 성능 개선 작업 - fetch join과 batch size 사용

 

테스트

  • 부하 테스트 - Artillery를 통해 게시글 조회 API 성능 개선 작업 진행, 더미데이터 유저 1000명, 게시글 1000개로 약 1만번 호출
  • 단위 테스트 - mockito를 이용해 mocking 하여 서비스 계층 단위테스트

 

📌잘했거나 좋았던 점/앞으로 계속 가져갈 점!

  1. JWT 및 Spring Security를 통한 인증 및 인가 구현
    이전 프로젝트에서는 (부끄럽지만) 아무 생각 없이 부트캠프에서 알려줬던 세션 처리 방식 그대로 사용했는데, 이번 JWT를 이용한 토큰 방식과 Spring Security를 활용한 인증 및 인가 방식을 채택해 구현하면서 이 둘의 차이와 각각 방식의 장/단점, 더불어 보안에 대한 내 가치관(?) 시야 트임 (?) 까지 깊게 이해할 수 있게 되어 좋았다.

  2. 단위 테스트 코드 작성
    테스트 코드의 중요성은 알고 있었지만 직접 작성해 본 적은 없었는데, 테스트 코드를 처음으로 작성해보면서 테스트 코드 작성 시 필요한 조건들에 대해 생각해 볼 수 있어서 좋았다. 외부의 영향을 받지 않고 특정 레이어의 코드만 돌아가게 하기 위해 Mockito를 학습했던 것도 좋은 경험이었고, give/when/then 문법을 적용시켜보고 각종 테스트에 관한 메서드를 적용해 본 경험도 재밌는 경험이었다. 앞으로 다른 프로젝트를 하게 된다면 TDD로 진행해 보고 싶다는 생각까지 하게 된 경험이었다.
    (제일 크게 깨달은 점.. 테스트 코드는 미리미리 쓰자 ^_^)

  3. 부하 테스트와 성능 개선 경험
    이번 프로젝트를 통해 얻은 가장 큰 성과인 것 같다. 부하 테스트 진행 후 원인을 파악하고, (말로만 듣던 N+1 문제였음) 이 원인에 대한 해결법을 학습하고, 해결해 가면서 느낀 점들이 많다. 내가 구현한 조회 API의 경우 페이지네이션을 적용했어서 batch size를 통해 문제를 해결했는데, 처음에 페이징 + fetch join이 만나면 더 큰 성능 저하를 내는 줄 모르고 진행했다가 당황했던 기억도 있어서 추후에 비슷한 문제가 생겼을 경우 좀 더 빠삭하게 학습한 후 적용해야겠다~와 같은 교훈을 얻은 경험이었다.
    (다음 조회 API를 구현할 때 비슷한 조건의 경우라면 바로 batch size나 fetch join을 사용하리 ...)

  4. 프로젝트의 전체 흐름 경험, 기여도 100%
    아무래도 팀 프로젝트의 경우 내가 담당하지 않은 기능에 대해선 코드가 어떻게 돌아가는지 잘 모르기 때문에 항상 이 부분에 있어서 갈증이 있었던 것 같다. 개인 프로젝트를 진행하면서는 기획부터 설계, 구현, 발생하는 많은 경우의 수/스택에 대한 모든 의사결정과 같은 것들을 내가 해서 편하거나 효율적인 면도 있었고, 반대로 많은 사람이 머리를 맞댔다면 나올 수 있었을 더 좋은 해결책도 있었을 것이다. 이렇게 기여도 100%의 개인 플젝을 해보면서 내가 해냈다는 성취감 + 팀원 존재의 소중함 까지 느꼈기 때문에 일석 多조의 경험이었다!!

 

📌아쉬운 점/개선할 점

  1. 배포
    docker를 통한 CI/CD 작업을 해보고 싶었는데, 부하테스트를 진행하면서 생각보다 해결하는 데 시간을 써서 화면단을 다 완성하지 못했다. 그래서 배포를 못해본 부분이 굉장히 아쉽고 다음 프로젝트를 진행하면서는 배포와 더불어 사용자 피드백까지 받아서 개선해보는 경험을 해보고 싶다.

  2. 더 세심한 깃 관리
    혼자서 진행한 개인 플젝이었기 때문에 사실 처음에는 세세한 깃 관리가 필요 없다고 생각해서, 그냥 커밋 컨벤션과 메세지만 신경쓰면서 커밋만 했었다. 하지만 Issue 기능에 대한 부분을 플젝 하는 도중 알게 되었고, Issue 기능까지 적용해서 커밋 관리를 했다면 더 깔끔했을 것 같다.. 라는 생각이 들어서 아쉬웠다. 다음 플젝때는 꼭 이 기능을 써보고 싶다.

  3. 토큰 값이 들어간 부하 테스트 해보기
    조회 API 단일 테스트 말고도, 토큰 값을 뽑아내는 과정이 포함된 테스트라던지 실제 서비스 환경과 비슷하게 테스트 시나리오를 구성해서 성능 테스트를 해보고 싶었는데 일정이 촉박해서 하지 못했다. 다음 프로젝트 때 테스트를 진행하게 된다면 Artillery 툴 뿐만 아니라 Jmeter나 사용해보지 않은 다른 테스트 툴을 학습해서 시나리오에 있어 더 자세한 환경 설정을 해보고 싶다.

 

📌마치며

이 회고 게시글을 쓰면서, 이 개인 프로젝트를 진행하기 전에 비해서 내가 많이 성장함을 느껴 기분이 좋았다. 동시에 아직 개발자로서 배우고 싶은 부분도 많고 여기서 멈추지말고 더더욱 열심히 해야겠다를 느꼈던 순간이기도 했다. 

 

다음 프로젝트를 끝낼 때도 회고 게시글을 비슷하게 쓰면서, 이 게시글과 비교했을 때 개선할 점들이 모두 개선된 내 모습을 이 포스팅을 보시는 독자(?) 분들에게 약속하면서 회고 글을 마친다. 고생했다 이굥진 ~

 

아 진짜 개발자 하고싶다.