- 개인 스터디 룸을 시간로 단위 대여 해주는 서비스입니다.
- 등록하는 사람이 스터디 룸을 등록합니다.
- 위치와 카테고리를 통한 장비에 관한 내용과 사진도 추가적으로 등록합니다.
- 사용자는 원하는 분류를 통해 스터디 룸 목록을 볼수 있습니다.
- 장비 관련된 분류를 제공합니다.
- 등록자는 등록 하면서 해당 방에 대한 시간당 이용시간을 표기해야합니다.
- 사용자는 원하는 방을 예약 가능한 시간에 예약 할 수 있습니다.
- 예약 이후 결제 시스템을 통해 결제를 진행합니다.
- 리뷰를 통해 후기를 남길 수 있다.
- 호스트는 장기 여행을 하게 되거나 집을 오랫동안 비울 수 있는 상황에 개인 공간을 대여함으로써 추가 수익을 창출할 수 있습니다.
- 제목, 본문, 꼬리말
Tag : subject
body
footer- 타입은 태그와 제목으로 구성되고, 태그는 영어로 쓰되 첫 문자는 대문자로 한다.
태그: 제목의 형태이며,:뒤에만 space가 있음에 유의한다.
Feat: 새로운 기능 추가Fix: 버그 수정Docs: 문서 수정Style: 코드 포맷팅, 세미콜론 누락, 코드 변경이 없는 경우Refactor: 코드 리펙토링Test: 테스트 코드, 리펙토링 테스트 코드 추가Chore: 빌드 업무 수정, 패키지 매니저 수정
Feat: "회원 가입 기능 구현"
SMS, 이메일 중복확인 API 개발
Resolves: #123
Ref: #456
Related to: #48, #45Directory 구조
└─src
├─main
│ ├─java
│ │ └─com
│ │ └─example
│ │ └─airdns
│ │ ├─domain
│ │ │ ├─deleteinfo
│ │ │ │ ├─entity
│ │ │ │ ├─repository
│ │ │ │ └─service
│ │ │ ├─equipment
│ │ │ │ ├─controller
│ │ │ │ ├─dto
│ │ │ │ ├─entity
│ │ │ │ ├─exception
│ │ │ │ ├─repository
│ │ │ │ └─service
│ │ │ ├─equipmentcategory
│ │ │ │ ├─entity
│ │ │ │ ├─repository
│ │ │ │ └─service
│ │ │ ├─image
│ │ │ │ ├─converter
│ │ │ │ ├─dto
│ │ │ │ ├─entity
│ │ │ │ ├─exception
│ │ │ │ ├─repository
│ │ │ │ └─service
│ │ │ ├─like
│ │ │ │ ├─controller
│ │ │ │ ├─dto
│ │ │ │ ├─entity
│ │ │ │ ├─exception
│ │ │ │ ├─repository
│ │ │ │ └─service
│ │ │ ├─oauth2
│ │ │ │ ├─common
│ │ │ │ ├─dto
│ │ │ │ ├─exception
│ │ │ │ ├─handler
│ │ │ │ ├─repository
│ │ │ │ ├─service
│ │ │ │ └─social
│ │ │ ├─payment
│ │ │ │ ├─config
│ │ │ │ ├─controller
│ │ │ │ ├─dto
│ │ │ │ ├─entity
│ │ │ │ ├─exception
│ │ │ │ ├─repository
│ │ │ │ └─service
│ │ │ ├─reservation
│ │ │ │ ├─controller
│ │ │ │ ├─dto
│ │ │ │ ├─entity
│ │ │ │ ├─exception
│ │ │ │ ├─repository
│ │ │ │ ├─service
│ │ │ │ └─servicefacade
│ │ │ ├─restschedule
│ │ │ │ ├─entity
│ │ │ │ ├─exception
│ │ │ │ ├─repository
│ │ │ │ └─service
│ │ │ ├─review
│ │ │ │ ├─controller
│ │ │ │ ├─dto
│ │ │ │ ├─entity
│ │ │ │ ├─exception
│ │ │ │ ├─repository
│ │ │ │ └─service
│ │ │ ├─room
│ │ │ │ ├─controller
│ │ │ │ ├─converter
│ │ │ │ ├─dto
│ │ │ │ ├─entity
│ │ │ │ ├─exception
│ │ │ │ ├─repository
│ │ │ │ ├─service
│ │ │ │ └─servicefacade
│ │ │ ├─roomequipment
│ │ │ │ ├─entity
│ │ │ │ ├─repository
│ │ │ │ └─service
│ │ │ └─user
│ │ │ ├─controller
│ │ │ ├─dto
│ │ │ ├─entity
│ │ │ ├─enums
│ │ │ ├─exception
│ │ │ ├─repository
│ │ │ └─service
│ │ └─global
│ │ ├─advice
│ │ ├─awss3
│ │ ├─common
│ │ │ ├─dto
│ │ │ └─entity
│ │ ├─config
│ │ ├─cookie
│ │ ├─exception
│ │ ├─jwt
│ │ ├─loadbalance
│ │ ├─redis
│ │ ├─scheduler
│ │ └─security
│ └─resources
│
└─testSoft Delete
-
도입 이유
- 방이 삭제되어도 해당 정보에 대해 접근 할 수 있는 방법이 필요
-
문제 상황
- 방이 삭제될 경우에 예약 목록에서 방에 대한 정보를 확인할 수 없는 문제 상황 발생
-
해결 방안
- is_deleted, deleted_at 컬럼을 추가함
-
의사 조율
-
Entity에 @SqlDelete를 사용하여 jpaRepository.delete 메소드 사용으로 Soft 삭제
delete 메소드를 사용하는 순간, 해당 Table의 row가 삭제되는 것이 아닌 SqlDelete문에서 활용한 update문이 실행되어 실제로 삭제된 것처럼 보이게 함
-
jpaRepository.delete를 Soft 삭제로 오버라이드하는 방법
Interface에 @Query를 달거나, 직접 쿼리를 작성하는 방안을 통해 삭제된 것처럼 보이게 함
-
-
의견 결정
- @SqlDelete를 사용한 Soft Delete 도입으로 실제 데이터 삭제를 유예하기로 함
- 스케줄러를 통해 특정 기간이 지난 Table의 row에 대해서는 별도의 데이터베이스에 적재 후 Table에서 완전 삭제하는 기능을 추가
- 완전 삭제된 데이터는 별도 데이터베이스에 적재되어 있기에 조회할 수 있는 기능
예약 동시성
- 도입 이유
- 한 사람이 예약 버튼을 누르는 시점에서 다른 사람이 해당 시간대의 예약을 막아야 한다고 생각했기 때문에 도입했습니다.
- 문제 상황
- 동시에 예약하기 버튼을 눌렀을 때 데이터의 정합성을 보장을 못하는 문제 발생
- 해결 방안
- Synchronized
- 자바의 Synchronized는 하나의 프로세스 안에서만 보장이 된다.
- Mysql에서 제공하는 Lock
- Pessimistic Lock
- Optimistic Lock
- Named Lock
- 레디스
- Lettuce
- Redisson
- Synchronized
- 의사 조율
- 현재 아키텍쳐는 분산 서버가 아니기 때문에 Synchronized 메서드도 가능하다고 판단 했지만, 서비스 확장성에는 안맞다고 생각했습니다.
- Mysql에서 제공하는 Lock 중에서 Named Lock은 트랜잭션 종료 시에 락 헤제, 세션 관리를 잘해줘야 하고, 현재 하나의 데이터 소스를 사용하는 중이라 적절하지 않다고 판단했습니다.
- Redis와 Mysql 을 비교 했을 때, 현재 Refresh Token을 이용하기 때문에 Redis를 구축하는데 필요한 비용이 없다고 생각을 했고, 성능상 mysql보다 redis가 좋기 때문에 추후 고도화 작업도 고려해 보면 Redis가 더 적합하다고 생각했습니다.
- Redis 라이브러리 중에서 Lettuce 같은 경우 스핀 락 방식이기 때문에 부하가 많이 갈거 같다고 생각했고, Redisson이 더 적합하다고 생각했습니다.
- 의견 결정
- 여러 요건을 따졌을 때, Redisson 방식이 가장 적절하다고 생각해 Redisson을 이용해 동시성을 제어 했습니다.
Application Load Balancer
- 도입이유
- Mixed Contents 문제 해결을 위해
- 문제 상황
- 로그인 로직 중 refresh Token을 통해 토큰 탈취와 같은 보안적인 부분을 보완하는 방향으로 결정했습니다.
- Route53을 통해 도메인을 구매하고 AWS Certificate Manager를 통해 해당 도메인에 대한 인증서를 발급받고 Cloud Front에 인증서를 적용한 상황에서 백엔드 서버에 API 요청을 보낼 때 Mixed Content 에러가 발생
- Https가 적용된 사이트에서 HTTP 요청을 전송할 경우 보안이 적용된 사이트에서 더 낮은 사이트로 연결 시도 하는 것이라 block이 발생
- 해결 방안
- EC2 인스턴스에 인증서 적용
- 인스턴스 1개만 사용하는 경우 ec2 인스턴스에 직접 SSL 인증서를 적용하여 HTTPS로 통신
- Nginx를 이용해 인증서 검증
- 프록시 서버를 생성하고 cloud front와 인증서를 검증하고 EC2 인스턴스와의 통신을 보안화함으로써 해결 방식
- Application Load Balancer
- Elastic Load Balancer 중 Application Load Balancer를 이용해 Https로 연결된 cloudFront와 인증서를 검증하고 EC2 인스턴스와 통신을 Http로 변경
- 의사 조율
- 현재 상황은 이미 AWS Certification Manager를 통해 인증서를 발급받았고, 서비스는 도커와 도커 컴포즈를 통해 확장성에 대한 이점을 챙긴 상황
- Nginx를 이용하면 별도의 설정이 필요하다는 불편함과 프로젝트 기간이 짧은 점을 고려해서 배제했습니다.
- 의견 결정
- 간단하게 Elastic Load Balancer를 이용한 확장성도 고려한 선택이 합리적이라고 생각했습니다.
프론트
- 도입 이유
- 프로젝트 결과를 시각적으로 확인하기 위해 프론트 단의 웹페이지 화면이 필요하다고 생각했습니다.
- 문제 상황
- 팀원 중 아무도 프론트에 대한 지식이 없고 경험이 없다.
- API 형식으로 프론트와 백엔드 송/수신 하기로 기획
- 해결 방안
- Thymeleaf
- Vue
- React
- 의사 조율
- Thymelea는 기획 단계에서 API 통신 한다는 전제로 했기 때문에 적절하지 않다는 생각에 배제 했습니다.
- Vue와 React 중 고민하다 러닝 커브가 더 낮은 프레임워크를 사용하기로 결정
- 의결 결정
- Vue는 Html Css Js 구조와 비슷하여 Js로 모든 작업을 하는 리액트보다 더 직관적이고 러닝커브가 더 낮다고 생각해 vue를 채택했습니다.
CI/CD
- Jenkins & Git Action
- Jenkins는 다양한 플러그인 존재, 수 많은 커뮤니티가 존재하지만 플러그인 하나를 설정할 때마다 새로운 지식이 필요하다는 단점이 있음
- Git Action은 형상 관리 도구인 Git과 연동이 편리하다는 장점이 존재하나 한정적인 플러그인만 존재한다는 단점이 있음
- 프로젝트 규모를 비교하여 다양한 플러그인을 가지고 있는 Jenkins의 플러그인들까지 사용할 필요가 없다고 느끼고 설정 난이도가 상대적으로 낮은 Git Action이 사용하는 것이 좋다고 판단하여 Git Action 사용
- Docker Compose
- 도커 컴포즈를 통해 두개의 컨테이너를 손쉽게 관리할 수 있다고 판단 했고, 분산 서버로 인해 여러개의 컨테이너가 생길 경우도 생각해서 도커 컴포즈를 채택했습니다.
반복되는 작업 공통화
Jackson의 기본 datetimeFormat 설정LocalDateTime타입 변수를 Dto로 받을 때마다,@JsonFormat을 적어줘야했음.- 반복적이고 불필요한 작업이라 판단했기 때문에 자주 쓰이는
yyyy-MM-dd HH:mm:ss를 공통 데이터 포맷으로 설정하고, 전역 설정에 추가
@EnableJpaAuditing사용- createdAt, ModifiedAt 은 반복적으로 사용하는 DB 데이터라고 판단하여 Auditing 기능을 통해 동시 적용하도록 변경
Swagger적용- API 문서화를 위해 드는 비용이 너무 크다고 판단해
Swagger를 적용하여 자동으로 문서화 되도록 함
- API 문서화를 위해 드는 비용이 너무 크다고 판단해





