개인 프로젝트 스마트 스토어 리뷰관리 시스템
리바이럴 프로젝트 팀 프로젝트
프로젝트 소개 및 주제 선정 이유
‘REVIRAL’은 스마트 스토어 판매자와 리뷰어를 연결하는 리뷰 관리 시스템입니다. 부업을 찾던 중, 구매평을 남겨주는 리뷰어 활동이 있다는 것을 알게 되었고, 이를 체계적으로 관리할 수 있는 시스템의 필요성을 느껴 프로젝트를 진행하게 되었습니다.
기존의 리뷰 체험단 서비스(예: 레뷰코퍼레이션)는 리뷰어에게 진입장벽이 높고, 판매자에게는 리뷰 비용 부담이 큰 문제가 있었습니다. 이러한 이유로 많은 리뷰 거래가 비공식적인 오픈채팅방을 통해 진행되고 있으며, 특히 선구매 후 리뷰비 지급 방식과 익명성을 기반으로 한 거래 구조로 인해 판매자가 리뷰어에게 정당한 리뷰비를 지급하지 않는 사례가 빈번하게 발생하고 있습니다.
REVIRAL은 이러한 문제를 해결하고자 리뷰어와 판매자 간의 안전하고 효율적인 리뷰 거래 환경을 구축하는 것을 목표로 합니다. 이를 통해 리뷰어는 안정적인 보상을 받을 수 있고, 판매자는 신뢰할 수 있는 리뷰 관리 시스템을 이용할 수 있도록 설계되었습니다.
프로젝트 소개
- Smart Store Review System: 판매자가 스마트 스토어 시스템의 상위 노출을 위해 상품 리뷰를 구매자에게 리뷰 비용을 지급하고 리뷰어는 리뷰를 남기는 시스템
- Github : 리바이럴https://github.com/BE-AlexKim/reviral), 클린코드 리팩토링 리바이럴
- 인원 : FE(1), BE(1)
- 기술 스택: Kotlin, MySQL, Redis, Spring Boot, AWS S3, Swagger UI, JPA, Putty, Filezlia, JWT, QueryDSL
- 기간 : 2024.12 - 2025.01
기술 상세
배포 비용 절감 : CI/CD 자동화보단 Filezlia와 PuTTY로
-
도입 배경
- CI/CD 자동화는 배포 과정의 편의성과 안정성 측면에서 많은 이점을 제공하지만, 이번 프로젝트는 서버 1대의 소규모 운영 환경과 비용 효율성을 우선으로 고려했습니다.
- Jenkins, GitHub Actions, AWS CodeBuild 등 CI/CD 시스템 도입 시 발생하는 비용과 관리 리소스 부담이 오히려 과한 투자라고 판단해, FileZilla와 PuTTY를 활용한 수동 배포 방식을 선택했습니다.
-
사용 이유
- 비용 절감: 별도의 CI/CD 시스템 없이 운영 가능
- 직관적이고 간편한 배포: 필요한 파일만 교체하면 배포 완료
- 실시간 관리 및 대응 가능: SSH로 서버 접속해 바로 수정 및 로그 확인
- 소규모 운영 환경에 최적화: 복잡한 시스템 없이 안정적인 서비스 관리 가능
-
성과
- 별도의 CI/CD 시스템 도입 및 유지 비용 없이 운영
- Jenkins, GitHub Actions, CodeBuild 등 인프라 비용 제거
- 소규모 프로젝트에 맞는 효율적인 비용 구조 확보
코드 리팩토링 : 클린 아키텍처로 프로젝트 구조 개선하기
-
도입 배경
- 초기 개발 단계에서는 빠른 개발을 위해 Seller, Reviewer, Admin의 회원가입과 로그인을 하나의 엔드포인트로 통합 처리했습니다. 그러나 기획 변경으로 역할별 로그인 방식과 정책이 달라지면서 한쪽(Seller)의 로직 변경이 다른 쪽(Reviewer)에 영향을 주는 문제가 발생했습니다. 이러한 구조적 한계를 해결하고자 역할별 책임을 분리하고 변경에 유연하게 대응하기 위해 SOLID 원칙을 적용하여 클린 아키텍처로 전환하였습니다.
-
사용 이유
- 외부 의존성이 제거된 구조 덕분에 순수한 비즈니스 Logic 만을 대상으로 한 단위 테스트가 쉬워지고 모의 데이터 (Mock)객체로 테스트를 진행할 수 있습니다.
- 역할과 책임이 명확히 분리되기 때문에, 새로운 기능 추가나 정책 변경이 발생하여도 기존 코드 영향이 최소화가 됩니다.
- 비즈니스의 핵심 로직 이 코드 변화에 영향을 받지 않고 독립적으로 유지되어 데이터 저장 방식이나, 외부 API 변경이 발생해도 핵심 로직은 그대로 유지할 수 있습니다.
-
성과
- 역할 마다 비즈니스 로직이 독립적으로 관리되어 특정 역할의 정책이나 로직이 바뀌어도 다른 역할에 영향 없이 수정이 가능하며 추가적인 비즈니스 확장이 가능해졌습니다.
- 테스트가 쉬워져서 배포 전 결함을 사전에 발견할 수 있어 장애 발생 가능성이 감소했습니다.
API 문서 작성 자동화 : Swagger UI 사용한 API 문서 자동화하기
-
도입 배경
- 이번 프로젝트는 프론트엔드 개발자와 온라인으로 소통하며 협업하는 구조로 진행되었기 때문에 API 명세서를 기존 방식처럼 문서 파일을 별도로 전달하거나 구두로 설명하는 방식은 API의 수정이나 추가가 발생할때마다 서버와 프론트간의 싱크를 맞추기가 어려웠습니다. 협업 효율을 떨어트리고 개발 생산성에도 영향을 줄 수 있는 구조적인 한계가 발생하여 Swagger UI를 도입하여 API 명세서를 확인하고, 테스트할 수 있는 환경을 제공하여 별도의 문서 요청이나 설명 없이도 API 구조와 응답을 명확하게 이해하고 개발할 수 있도록 진행하였습니다.
-
사용 이유
- 별도의 테스트 툴(Postman) 없이 실시간으로 사용 가능한 API 테스트를 지원해주기 때문에 API 테스트와 검증을 빠르게 할 수 있습니다.
- 코드와 함께 관리되기 때문에 문서로 명세화 할 경우보다 어긋나는 상황을 줄일 수 있어 유지보수에 유리합니다.
- API를 설명하거나, 변경 사항을 공지하는 부담이 줄어들고 추후 API Mocking, CodeGen과 같이 테스트 자동화 또는 반복 작업을 줄이기에 좋습니다.
-
개선 사항
-
가독성과 유지보수성이 떨어지는 Swagger Annotation
@Operation( summary = "사용자 기초정보 등록 API", description = "소셜 회원가입 시, 초기 기초정보(이름,성별,주소,휴대폰번호)를 등록한다.", requestBody = RequestBody( content = [ Content( mediaType = MediaType.APPLICATION_JSON_VALUE, examples = [ ExampleObject( name = "초기 정보 등록 요청 예시", value = """ { "userId": 1, "name": "이름", "phoneNumber": "01012345678", "password": "비밀번호(String)", "gender": "성별(MAN,WOMAN)", "isEvent": true } """ ) ] ) ] ) ) @PostMapping("/info/basic") fun setStandardUserInfo( @RequestBody request: BasicUserInfoRequestDTO ): ResponseEntity<WrapResponseEntity<Boolean>> { val basic = accountService.setBasicUserInfo(request) return WrapResponseEntity.toResponseEntity(HttpStatus.OK, "isSave", true) }
-
Controller에 핵심 비즈니스 로직과 무관한 문서용 코드가 너무 많아져 가독성이 떨어지고 재사용성과 수정 비용이 많이 발생하게 됩니다.
@BasicUserInfoExplain @PostMapping("/info/basic") fun setStandardUserInfo( @RequestBody request: BasicUserInfoRequestDTO ): ResponseEntity<WrapResponseEntity<Boolean>> { val basic = accountService.setBasicUserInfo(request) return WrapResponseEntity.toResponseEntity(HttpStatus.OK, "isSave", true) } @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) @Inherited @Operation( summary = "사용자 기초정보 등록 API", description = "소셜 회원가입 시, 초기 기초정보(이름,성별,주소,휴대폰번호)를 등록한다.", requestBody = RequestBody( content = [ Content( mediaType = MediaType.APPLICATION_JSON_VALUE, examples = [ ExampleObject( name = "초기 정보 등록 요청 예시", value = """ { "userId": 1, "name": "이름", "phoneNumber": "01012345678", "password": "비밀번호(String)", "gender": "성별(MAN,WOMAN)", "isEvent": true } """ ) ] ) ] ) ) annotation class BasicUserInfoExplain()
-
Swagger Annotation을 별도의
Annotation class
로 분리하여 생성하여 Controller의 가독성을 높이고, 추가적으로 공통으로 생성되는@ApiRespones
를 정의하여 재사용성을 높일 수 있었습니다.
-
-
성과
- 문서 전달 및 별도 설명 과정이 줄어들어 API 커뮤니케이션 과정에서 발생하던 소통 비용을 약 40% 절감되었습니다.
- API의 품질 관리 및 서비스 안정성을 강화할 수 있었습니다. API 테스트 및 검증 과정에서 오류 발견율이 높아졌고, 서비스 품질과 유지보수 효율성이 크게 향상되었습니다.
- 프론트엔드와 백엔드 간 싱크 맞추기 과정에서 발생하던 반복 작업이 줄어들었습니다.