Skip to content

Commit 21403d7

Browse files
gunwoong1630HwuanPagejohnhuh619garusitellCopilot
authored
relase (#111)
* git initialize * feature/swagger-03-gunwoong (#5) * feat: 공통 도메인 구현 * feat: 메인 어플리케이션에 추가 * feat: swagger 추가 * feat: swagger 추가 * feature/base domain 04 gunwoong (#6) * feat: 공통 도메인 구현 * feat: 메인 어플리케이션에 추가 * feature/OpenAPI Test/02-HwuanPage * feature/OpenAPI Test/02-HwuanPage * Update SurfingForecastApiClient.java * feature/APICallTest-02-HwuanPage * feature/EntityInit-13-HwuanPage * feature/EntityInit-13-HwuanPage * feature/JellyfishEntityInit-13-HwuanPage * Update FishingType.java * feature/EntityInitialize-13-HwuanPage * feat: entity, repositor 구현 * feat: 예상 dto 구현 * chore: 의존성 추가 * feat: 로그인 구현 & 이후 토큰 발급 로직 구현 * fix: AuthCotnroller 수정 * fix: 클라이언트에서 카카오에서 코드를 받아 서버로 post 하게 수정 * feat: 토큰 검증 * feat: refresh token 블랙리스트 처리 로직 구현 * feat: refresh 토큰 블랙리스트 처리 & 재발급 로직 구현 * feat: SecurityFilterChain 엔드 포인트 허용 * feat: refresh 토큰 블랙리스트 검증 로직 구현 * feat: redis에서 refreshToken 블랙리스트 검증 * refactor: controller에 강하게 결합 되어 있던 로직들 분리 * test: member 관련 테스트 * chore: 하드코딩한 중요 값 Intellij IDEA 환경변수로 설정 * refactor: state 관리를 위해 세션 추가 * feat: member 정보 조회하는 서비스 로직 구현 * feat: member 정보 조회하는 서비스 로직 구현 * format: naver formatter로 포매팅 * chore: application-dev * fix: customException 처리 * Feat/meeting interface (#19) * feat : MeetingService 인터페이스 구현 * feat : ParticipantResponse * feat : MeetingListResponse 구현 * feat : MeetingDetailResponse구현 * feat : MeetingDetailAndMemberResponse 구현 * feat : ListSpot 구현 * feat : DetailSpot 구현 * feat : CreateMeetingRequest 구현 * feat : Tag 구현 * feat : Long -> long 변경 서비스와 Entity내에서 null값이 절대 나오지 않는다고 판단하는 값을 long으로 변경하였습니다. * feat : MeetingService.java -> 무한페이지로딩형식으로 바꾸었습니다. * Update src/main/java/sevenstar/marineleisure/meeting/Dto/Response/MeetingDetailResponse.java Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]> * Feature/FavoritesAndAlertInterface-16-HwuanPage * feature/FavoritesAndAlertInterface-16-HwuanPage * Update AlertMapper.java * Update JellyfishRegionDensityRepository.java * Update AlertController.java * Update FavoriteController.java * Update FavoriteRepository.java * Update AlertController.java * Update JellyfishSpieces.java * Update JellyfishRegion.java * Update JellyfishRegion.java * feature/CustomExceptionInit-22-HwuanPage * feature/CustomExceptionInit-22-HwuanPage * Errorcode interface Change * Refactor application.yml 환경변수 설정 (#25) * refactor: application.yml 환경변수 설정 * Rename: 오타 수정 * Feature/spot service interface 29 gunwoong (#30) * feat: api * feat: api 스케줄링 * feat: spot service inteface * Feature/api scheduler 15 gunwoong (#28) * feat: api * feat: api 스케줄링 * feat: spot service inteface * test: remove legacy test * feat: apply open meteo * test: apply api test * feat: spot service (#34) * feat: spot service * feat: spot service 고도화 및 조회도 관련 서비스 추가 * feat: 조회도 관련 서비스 추가 * feat: 조회도 관련 서비스 추가 * feat: 조회도 관련 서비스 추가 * hotfix: duplicated controller method * feature/FavoriteCRUD-33-HwuanPage * DELETE COMPLETE * UPDATE COMPLETE * search COMPLETE * Before gunwoong * FavoriteCRUD create * feat/domain test * FavoriteSpotServiceTest * FavoriteSpotServiceTest * feature/FavoriteCURD-33-HwuanPage * add some description on service * Update FavoriteServiceImpl.java * Feature/spot preview 40 gunwoong (#41) * feat: spot preview & 리팩토링 * feat: spot preview & 리팩토링 * hotfix: jpa metamodel fix * fix: error fix * fix: 소셜 로그인 재시도 시 닉네임 UNIQUE 제약 위반 오류 발생 (#42) * fix: 로그아웃 후 재로그인 시 동일 정보로 db에 insert 하던 버그 수정 * refactor: 로그아웃 후 재로그인 시 동일 정보로 db에 insert 수정 사항 리팩토링 * test: 변경사항에 따른 테스트 코드 수정 * hofix: bug fix * Feature/Alert-22-HwuanPage * Create Pdf Parser * Web crawler run perpectly,but pdfparser do not work well * PDF parse to stack DB complete with OPENAI * CallAlert Complete * JellyFish PDF parsing work well * feature/ControllerTest Complete * feature/JellyfishAlert-26-HwuanPage * feat: 즐겨찾기 추가 및 리팩토링 (#49) * feat: 즐겨찾기 추가 및 리팩토링 * refactor: 리팩토링 * feat: 카카오 로그인을 stateless 하게 변경한다 (#51) * refactor: 기존 state 사용 방식 -> stateless 방식으로 변경 * refactor: 기존 state 사용 방식 -> stateless 방식으로 변경으로 인해 필요 없는 엔드 포인트 삭제 * test: 변경사항 test 수정 * feat: 카카오 측에서 인증 실패시에 반환 하는 에러 처리하는 코드 구현 * test: 카카오 측에서 인증 실패시에 반환 하는 에러 처리하는 테스트 추가 * fix: 주석 제거 * fix: exception 변경 * Feat/meeting service (#46) * WIP: Rebase를 위한 임시 저장 * feat : Meeting.java -> Meeting 엔터티 @builder 를 상위 어노테이션에 일단 추가시켰습니다. * feat : Meeting.java -> Meeting 엔터티 @builder 를 상위 어노테이션에 일단 추가시켰습니다. * feat : Meeting.java -> Meeting 엔터티 @builder 를 상위 어노테이션에 일단 추가시켰습니다. * Delete MeetingServiceImplReview.md * Delete MeetingServiceUserFlow.md * feat : 패키지명 변경 이슈 -> 패키지 명을 컨벤션에 따른 이름으로 변경했습니다. * feat : MeetingController.java long participantCount = participantRepository.countMeetingIdMember -> long participantCount = participantRepository.countMeetingId로 수정하였습니다. * feat : MeetingError.java MeetingError.java 를 추가하였습니다. * feat : MeetingMapper MeetingServiceImpl에서 사용중이었던 Mapper를 분리하였습니다. * feat : MeetingService.java 패키지 명 수정으로 인해서 수정사항이 있었습니다. * feat : MeetingServiceImpl.java 트랜잭션 관리 명확화 하였습니다. validate 패키지를 개선하였습니다. joinMeeting 중복 참여 제한 로직을 강화하였습니다. * `getAllMeetings(Long cursorId, int size)`: * 목적: 모든 모임 목록을 페이징 처리하여 조회합니다. cursorId를 사용하여 무한 스크롤과 같은 커서 기반 페이징을 지원합니다. * 특징: @transactional(readOnly = true)를 통해 읽기 전용 트랜잭션으로 최적화되었습니다. * `getMeetingDetails(Long meetingId)`: * 목적: 특정 모임의 상세 정보를 조회합니다. 호스트, 장소, 태그 등 연관된 정보를 함께 가져옵니다. * 개선 예정: 현재 N+1 문제가 발생할 수 있어, 향후 Fetch Join을 통한 성능 최적화가 필요합니다. * `getStatusMyMeetings(Long memberId, Long cursorId, int size, MeetingStatus meetingStatus)`: * 목적: 특정 회원의 상태별(예: 모집 중, 완료) 모임 목록을 페이징 처리하여 조회합니다. * `getMeetingDetailAndMember(Long memberId, Long meetingId)`: * 목적: 호스트가 자신의 모임 상세 정보와 참여자 목록을 조회합니다. 참여자들의 닉네임을 함께 제공합니다. * `countMeetings(Long memberId)`: * 목적: 특정 회원이 참여한 모임의 총 개수를 반환합니다. * `joinMeeting(Long meetingId, Long memberId)`: * 목적: 회원이 특정 모임에 참여합니다. * 주요 개선: 모임 상태 검증(verifyRecruiting), 중복 참여 검증(`verifyNotAlreadyParticipant`), 모임 정원 초과 검증(verifyMeetingCount) 로직이 강화되었습니다. * 개선 예정: 동시성 문제(Race Condition) 해결을 위한 비관적 락(Pessimistic Lock) 적용이 필요합니다. * `leaveMeeting(Long meetingId, Long memberId)`: * 목적: 회원이 모임에서 탈퇴합니다. * 주요 개선: 호스트 탈퇴 방지(verifyNotHost), 모임 상태에 따른 탈퇴 가능 여부 검증(verifyLeave) 로직이 추가되었습니다. * 개선 예정: MEETING_NOT_FOUND 대신 CANNOT_LEAVE_COMPLETED_MEETING과 같은 더 구체적인 에러 코드 적용이 필요합니다. * `createMeeting(Long memberId, CreateMeetingRequest request)`: * 목적: 새로운 모임을 생성합니다. 호스트를 참여자로 자동 등록하고 태그 정보를 저장합니다. * `updateMeeting(Long meetingId, Long memberId, UpdateMeetingRequest request)`: * 목적: 기존 모임의 정보를 수정합니다. 호스트만 수정할 수 있도록 검증합니다. * `deleteMeeting(Member member, Long meetingId)`: * 목적: 모임을 삭제합니다. * 개선 예정: 물리적 삭제 대신 논리적 삭제(Soft Delete) 방식 도입을 고려 중입니다. * feat : MeetingValidate.java,MemberValidate.java,ParticipantValidate,SpotValidate,TagValidate.java 검증로직을 추가하였습니다. * feat : MemberError.java , ParticipantRepository 기능을 추가하였습니다. --------- Co-authored-by: Hwang Seong Cheol a.k.a Hwuan Page <[email protected]> * Feature/integration init (#54) * feature/IntegrationSet(test&Build)-52-HwuanPage * data.sql unique update * image build needs * ignore dev.yml * remove dev.yml tracking and ignore it * prod * proded * Feature/activities 17 audwls239 (#56) * feature: 컨트롤러, 서비스 생성 * feature: 활동별 지수 조회(위치 기반) * feature: DTO 추가 * feature: 활동별 지수 조회(글로벌) 추가, 컨트롤러 수정 * feature: 활동별 지수 상세 조회(미완성) * feature: 해양 정보 조회 * feature: 활동 상세 조회 --------- Co-authored-by: Gunwoong cho <[email protected]> * feat : ParticipantError 입니다. * hotfix: error fix * fix : Directory 수정사항입니다. (#57) * hotfix: error fix * feat: member delete (#58) * fix: 멤버 삭제 구현 * feat: 멤버 삭제, 위/경도 수정 구현 * test: 테스트 수정 * Delete src/main/java/sevenstar/marineleisure/meeting/repository/MemberRepository.java * Delete src/main/java/sevenstar/marineleisure/meeting/repository/OutdoorSpotSpotRepository.java * Delete src/main/resources/test.http --------- Co-authored-by: LEESUNBIN <[email protected]> Co-authored-by: Gunwoong cho <[email protected]> * fix : ParticipantRepository (#59) existsByMeetingIdAndUserId 로 수정하였습니다. * fix : ParticipantRepository (#60) memberId -> userId로 수정하였습니다. * fix: token (#61) * feature/base domain 04 gunwoong (#6) * feat: 공통 도메인 구현 * feat: 메인 어플리케이션에 추가 * feature/CustomExceptionInit-22-HwuanPage * feature/CustomExceptionInit-22-HwuanPage * Errorcode interface Change * Refactor application.yml 환경변수 설정 (#25) * refactor: application.yml 환경변수 설정 * Rename: 오타 수정 * Feature/spot service interface 29 gunwoong (#30) * feat: api * feat: api 스케줄링 * feat: spot service inteface * feat: 카카오 로그인을 stateless 하게 변경한다 (#51) * refactor: 기존 state 사용 방식 -> stateless 방식으로 변경 * refactor: 기존 state 사용 방식 -> stateless 방식으로 변경으로 인해 필요 없는 엔드 포인트 삭제 * test: 변경사항 test 수정 * feat: 카카오 측에서 인증 실패시에 반환 하는 에러 처리하는 코드 구현 * test: 카카오 측에서 인증 실패시에 반환 하는 에러 처리하는 테스트 추가 * fix: 주석 제거 * fix: exception 변경 * Feat/meeting service (#46) * WIP: Rebase를 위한 임시 저장 # Conflicts: # src/main/java/sevenstar/marineleisure/global/exception/enums/CommonErrorCode.java # src/main/java/sevenstar/marineleisure/global/swagger/SwaggerController.java * feat : Meeting.java -> Meeting 엔터티 @builder 를 상위 어노테이션에 일단 추가시켰습니다. * feat : Meeting.java -> Meeting 엔터티 @builder 를 상위 어노테이션에 일단 추가시켰습니다. * feat : Meeting.java -> Meeting 엔터티 @builder 를 상위 어노테이션에 일단 추가시켰습니다. * Delete MeetingServiceImplReview.md * Delete MeetingServiceUserFlow.md * feat : 패키지명 변경 이슈 -> 패키지 명을 컨벤션에 따른 이름으로 변경했습니다. * feat : MeetingController.java long participantCount = participantRepository.countMeetingIdMember -> long participantCount = participantRepository.countMeetingId로 수정하였습니다. * feat : MeetingError.java MeetingError.java 를 추가하였습니다. * feat : MeetingMapper MeetingServiceImpl에서 사용중이었던 Mapper를 분리하였습니다. * feat : MeetingService.java 패키지 명 수정으로 인해서 수정사항이 있었습니다. * feat : MeetingServiceImpl.java 트랜잭션 관리 명확화 하였습니다. validate 패키지를 개선하였습니다. joinMeeting 중복 참여 제한 로직을 강화하였습니다. * `getAllMeetings(Long cursorId, int size)`: * 목적: 모든 모임 목록을 페이징 처리하여 조회합니다. cursorId를 사용하여 무한 스크롤과 같은 커서 기반 페이징을 지원합니다. * 특징: @transactional(readOnly = true)를 통해 읽기 전용 트랜잭션으로 최적화되었습니다. * `getMeetingDetails(Long meetingId)`: * 목적: 특정 모임의 상세 정보를 조회합니다. 호스트, 장소, 태그 등 연관된 정보를 함께 가져옵니다. * 개선 예정: 현재 N+1 문제가 발생할 수 있어, 향후 Fetch Join을 통한 성능 최적화가 필요합니다. * `getStatusMyMeetings(Long memberId, Long cursorId, int size, MeetingStatus meetingStatus)`: * 목적: 특정 회원의 상태별(예: 모집 중, 완료) 모임 목록을 페이징 처리하여 조회합니다. * `getMeetingDetailAndMember(Long memberId, Long meetingId)`: * 목적: 호스트가 자신의 모임 상세 정보와 참여자 목록을 조회합니다. 참여자들의 닉네임을 함께 제공합니다. * `countMeetings(Long memberId)`: * 목적: 특정 회원이 참여한 모임의 총 개수를 반환합니다. * `joinMeeting(Long meetingId, Long memberId)`: * 목적: 회원이 특정 모임에 참여합니다. * 주요 개선: 모임 상태 검증(verifyRecruiting), 중복 참여 검증(`verifyNotAlreadyParticipant`), 모임 정원 초과 검증(verifyMeetingCount) 로직이 강화되었습니다. * 개선 예정: 동시성 문제(Race Condition) 해결을 위한 비관적 락(Pessimistic Lock) 적용이 필요합니다. * `leaveMeeting(Long meetingId, Long memberId)`: * 목적: 회원이 모임에서 탈퇴합니다. * 주요 개선: 호스트 탈퇴 방지(verifyNotHost), 모임 상태에 따른 탈퇴 가능 여부 검증(verifyLeave) 로직이 추가되었습니다. * 개선 예정: MEETING_NOT_FOUND 대신 CANNOT_LEAVE_COMPLETED_MEETING과 같은 더 구체적인 에러 코드 적용이 필요합니다. * `createMeeting(Long memberId, CreateMeetingRequest request)`: * 목적: 새로운 모임을 생성합니다. 호스트를 참여자로 자동 등록하고 태그 정보를 저장합니다. * `updateMeeting(Long meetingId, Long memberId, UpdateMeetingRequest request)`: * 목적: 기존 모임의 정보를 수정합니다. 호스트만 수정할 수 있도록 검증합니다. * `deleteMeeting(Member member, Long meetingId)`: * 목적: 모임을 삭제합니다. * 개선 예정: 물리적 삭제 대신 논리적 삭제(Soft Delete) 방식 도입을 고려 중입니다. * feat : MeetingValidate.java,MemberValidate.java,ParticipantValidate,SpotValidate,TagValidate.java 검증로직을 추가하였습니다. * feat : MemberError.java , ParticipantRepository 기능을 추가하였습니다. --------- Co-authored-by: Hwang Seong Cheol a.k.a Hwuan Page <[email protected]> * feat: 테스트용 액세스 토큰 생성 * feature/base domain 04 gunwoong (#6) * feat: 공통 도메인 구현 * feat: 메인 어플리케이션에 추가 * Refactor application.yml 환경변수 설정 (#25) * refactor: application.yml 환경변수 설정 * Rename: 오타 수정 * Feature/spot service interface 29 gunwoong (#30) * feat: api * feat: api 스케줄링 * feat: spot service inteface * feature/base domain 04 gunwoong (#6) * feat: 공통 도메인 구현 * feat: 메인 어플리케이션에 추가 * feature/CustomExceptionInit-22-HwuanPage * feature/CustomExceptionInit-22-HwuanPage * Errorcode interface Change * Feature/spot service interface 29 gunwoong (#30) * feat: api * feat: api 스케줄링 * feat: spot service inteface * Feature/api scheduler 15 gunwoong (#28) * feat: api * feat: api 스케줄링 * feat: spot service inteface * test: remove legacy test * feat: apply open meteo * test: apply api test * feature/FavoriteCRUD-33-HwuanPage * DELETE COMPLETE * UPDATE COMPLETE * search COMPLETE * Before gunwoong * FavoriteCRUD create * feat/domain test * FavoriteSpotServiceTest * FavoriteSpotServiceTest * feature/FavoriteCURD-33-HwuanPage * add some description on service * Update FavoriteServiceImpl.java * fix: error fix * fix: 소셜 로그인 재시도 시 닉네임 UNIQUE 제약 위반 오류 발생 (#42) * fix: 로그아웃 후 재로그인 시 동일 정보로 db에 insert 하던 버그 수정 * refactor: 로그아웃 후 재로그인 시 동일 정보로 db에 insert 수정 사항 리팩토링 * test: 변경사항에 따른 테스트 코드 수정 * hofix: bug fix * feat: 카카오 로그인을 stateless 하게 변경한다 (#51) * refactor: 기존 state 사용 방식 -> stateless 방식으로 변경 * refactor: 기존 state 사용 방식 -> stateless 방식으로 변경으로 인해 필요 없는 엔드 포인트 삭제 * test: 변경사항 test 수정 * feat: 카카오 측에서 인증 실패시에 반환 하는 에러 처리하는 코드 구현 * test: 카카오 측에서 인증 실패시에 반환 하는 에러 처리하는 테스트 추가 * fix: 주석 제거 * fix: exception 변경 * Feat/meeting service (#46) * WIP: Rebase를 위한 임시 저장 # Conflicts: # src/main/java/sevenstar/marineleisure/global/exception/enums/CommonErrorCode.java # src/main/java/sevenstar/marineleisure/global/swagger/SwaggerController.java * feat : Meeting.java -> Meeting 엔터티 @builder 를 상위 어노테이션에 일단 추가시켰습니다. * feat : Meeting.java -> Meeting 엔터티 @builder 를 상위 어노테이션에 일단 추가시켰습니다. * feat : Meeting.java -> Meeting 엔터티 @builder 를 상위 어노테이션에 일단 추가시켰습니다. * Delete MeetingServiceImplReview.md * Delete MeetingServiceUserFlow.md * feat : 패키지명 변경 이슈 -> 패키지 명을 컨벤션에 따른 이름으로 변경했습니다. * feat : MeetingController.java long participantCount = participantRepository.countMeetingIdMember -> long participantCount = participantRepository.countMeetingId로 수정하였습니다. * feat : MeetingError.java MeetingError.java 를 추가하였습니다. * feat : MeetingMapper MeetingServiceImpl에서 사용중이었던 Mapper를 분리하였습니다. * feat : MeetingService.java 패키지 명 수정으로 인해서 수정사항이 있었습니다. * feat : MeetingServiceImpl.java 트랜잭션 관리 명확화 하였습니다. validate 패키지를 개선하였습니다. joinMeeting 중복 참여 제한 로직을 강화하였습니다. * `getAllMeetings(Long cursorId, int size)`: * 목적: 모든 모임 목록을 페이징 처리하여 조회합니다. cursorId를 사용하여 무한 스크롤과 같은 커서 기반 페이징을 지원합니다. * 특징: @transactional(readOnly = true)를 통해 읽기 전용 트랜잭션으로 최적화되었습니다. * `getMeetingDetails(Long meetingId)`: * 목적: 특정 모임의 상세 정보를 조회합니다. 호스트, 장소, 태그 등 연관된 정보를 함께 가져옵니다. * 개선 예정: 현재 N+1 문제가 발생할 수 있어, 향후 Fetch Join을 통한 성능 최적화가 필요합니다. * `getStatusMyMeetings(Long memberId, Long cursorId, int size, MeetingStatus meetingStatus)`: * 목적: 특정 회원의 상태별(예: 모집 중, 완료) 모임 목록을 페이징 처리하여 조회합니다. * `getMeetingDetailAndMember(Long memberId, Long meetingId)`: * 목적: 호스트가 자신의 모임 상세 정보와 참여자 목록을 조회합니다. 참여자들의 닉네임을 함께 제공합니다. * `countMeetings(Long memberId)`: * 목적: 특정 회원이 참여한 모임의 총 개수를 반환합니다. * `joinMeeting(Long meetingId, Long memberId)`: * 목적: 회원이 특정 모임에 참여합니다. * 주요 개선: 모임 상태 검증(verifyRecruiting), 중복 참여 검증(`verifyNotAlreadyParticipant`), 모임 정원 초과 검증(verifyMeetingCount) 로직이 강화되었습니다. * 개선 예정: 동시성 문제(Race Condition) 해결을 위한 비관적 락(Pessimistic Lock) 적용이 필요합니다. * `leaveMeeting(Long meetingId, Long memberId)`: * 목적: 회원이 모임에서 탈퇴합니다. * 주요 개선: 호스트 탈퇴 방지(verifyNotHost), 모임 상태에 따른 탈퇴 가능 여부 검증(verifyLeave) 로직이 추가되었습니다. * 개선 예정: MEETING_NOT_FOUND 대신 CANNOT_LEAVE_COMPLETED_MEETING과 같은 더 구체적인 에러 코드 적용이 필요합니다. * `createMeeting(Long memberId, CreateMeetingRequest request)`: * 목적: 새로운 모임을 생성합니다. 호스트를 참여자로 자동 등록하고 태그 정보를 저장합니다. * `updateMeeting(Long meetingId, Long memberId, UpdateMeetingRequest request)`: * 목적: 기존 모임의 정보를 수정합니다. 호스트만 수정할 수 있도록 검증합니다. * `deleteMeeting(Member member, Long meetingId)`: * 목적: 모임을 삭제합니다. * 개선 예정: 물리적 삭제 대신 논리적 삭제(Soft Delete) 방식 도입을 고려 중입니다. * feat : MeetingValidate.java,MemberValidate.java,ParticipantValidate,SpotValidate,TagValidate.java 검증로직을 추가하였습니다. * feat : MemberError.java , ParticipantRepository 기능을 추가하였습니다. --------- Co-authored-by: Hwang Seong Cheol a.k.a Hwuan Page <[email protected]> * fix : jellyfish 부분 * fix: activity 부분 * fix: member 부분 * fix: member 부분 * fix: spot 부분 * fix: forecast 부분 * fix: favorite 부분 * fix: alert 부분 * fix: meeting 부분 --------- Co-authored-by: Gunwoong cho <[email protected]> Co-authored-by: Hwang Seong Cheol a.k.a Hwuan Page <[email protected]> Co-authored-by: MyungJin <[email protected]> Co-authored-by: LEESUNBIN <[email protected]> Co-authored-by: gunwoong <[email protected]> * hotfix/fix-alert&favorites-62-HwuanPage * fix(hotfix/Meeting) : rebase로 인한 코드 누락 수정 (#65) * hotfix: 코드 누락 해결 (#67) * Fix/fix 70 gunwoong (#71) * hotfix: fix * hotfix: fix * hotfix: fix * fix: application-prod.yml에서 쿠키를 쓸지 말지 결정할 수 있게 수정 (#69) * fix: application-prod.yml에서 쿠키를 쓸지 말지 결정할 수 있게 수정 * test: 테스트 코드 작성 * fix: activities 시큐리티 엔드포인트 허용. redirecturi 수정 * Chore/docker set andvariable-68-hwuanPage * chore/ReadytoDeployv1.0.0-68-HuwanPage * chore/ReadytoDeploymentv1.0.0-68-HuwanPage * remove etc * prod * refactor: blacklist 엔티티의 jti에 인덱스를 건다. (#74) * Feat/meeting test 75 (#77) * feat : Meetingtest 를 위한 Util 파일입니니다. * feat : Meetingtest 를 위한 Util 파일입니니다. * feat : MeetingServiceImplTest 단위테스트입니다. * feat : MeetingControllerTest 통합테스트입니다. * feat : Build Lombok을 테스트를 위한 수정입니다. * feat : Tag 엔티티 Tag List<String> content 를 변환하기 위한 파일입니다. * feat : MeetingServiceImpl * feat : MeetingServiceImpl에서 수정하는 응답을 수정 , 매퍼를 수정하였습니다. * feat : Meeting에서 필요한 url을 열어뒀습니다. * space prob solve * stack-trace-DEBUG * hotfix/data.sql deprecate-HwuanPage (#79) * hotfix/data.sql deprecate-HwuanPage * portnum fix * Xtest * test X * workflow fix * add id * fix docker-compose-image-root * release/v1-marineleisure * fix: blacklist 엔티티의 jti에 인덱스를 건다. (#83) * fix: cors 프론트엔드 배포 도메인 추가 (#84) * fix: blacklist 엔티티의 jti에 인덱스를 건다. * fix: cors 프로트엔드 도메인 추가 * hotfix/method_allowed_patch-HwuanPage (#86) * Refactor/exception hwuan page (#87) * refacotr/favorite-Exception-update * fix kakao_redirect_uri * Feature/map service refactoring 76 gunwoong (#85) * feat: mapServiceRefactoring * refactoring: spot detail refactoring * refactoring: GeoUtils refactoring * test: repository test disable for prod * fix: apply flyway to yml * fix: disable test * refactor: khoa refactoring * fix: bug * fix: sql * fix: yml 환경변수 추가 * fix: detail field name 수정 * feature: 스케줄링 비동기 구현 (#91) * refactor: cacheable (#103) * Fix/meeting urland role (#100) * fix : MeetingServiceImpl getStatusMeetings -> getStatusMeeting_role : Guest 인지 host 인지 판단하는 로직을 추가 * fix : MeetingRepository getStatusMeetings -> getStatusMeeting_role : Guest 인지 host 인지 판단하는 로직을 추가 * fix : MeetingError MEETING_MEMBER_NOT_FOUND 에러를 추가하였습니다. 미팅에서 맴버를 확인할 수 없는 에러입니다. * fix : MeetingController MeetingController 에서 role 을 확인하여 추가 확인할 수 있도록 하였습니다. * fix : MeetingServiceImplTest, MeetingControllerTest.java URL 개선에 의한 새로운 테스트 입니다. * fix : MeetingServiceImplTest, MeetingControllerTest.java @disabled 추가 하였습니다. * feat: 회원 탈퇴 시 카카오 연결 끊기도 수행하게 구현한다. (#98) * feat: member 삭제 시 kakao 연결 끊기 로직도 수행하게 구현 * test: 변경 사항 test * feat : Meeting의 커서방식에서 매핑을 하였습니다. (#94) * feat: 카카오 로그인 과정에서 pkce를 통해 보안 관점에서 개선 (#106) * feat: 보안 인증 과정에서 PKCE 추가하여 구현 * test: 변경 사항 test 추가 * feat: PKCE 기반 보안 기능 코드 구조 변경 * test: PKCE 기반 보안 기능 test * refactor: PKCE 생성을 클라이언트 에게 넘긴다. * test: pkce test 플로우 변경에 따라 변경 * fix: member entity의 nickname 중복을 허용한다 * fix: 테스트를 위해 SchedulerService.java 의 @RequiredArgsConstrucor 지운 부분 복구 * fix: 테스트를 위해 SchedulerService.java 의 @RequiredArgsConstrucor 지운 부분 복구 * refactor: open-meteo 서비스 관련 리팩토링 (#95) * refactor: RichDomain으로 변경 내역입니다. (#105) * Fix: login redirect (#107) * fix: 로그인 요청때의 리다이렉트 uri를 토큰 교환시에도 사용 * test: test * fix: fallback 상황에서 리다이렉트 uri 찾는 로직 추가 * Refactor/meeting rich domain (#110) * refactor: RichDomain으로 변경 내역입니다. * refactor: 누락 프로젝트 파일이 있어 첨부합니다. * build: caffenine 적용 --------- Co-authored-by: HwuanPage <[email protected]> Co-authored-by: JaeoneHeo <[email protected]> Co-authored-by: LEESUNBIN <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: MyungJin <[email protected]> Co-authored-by: iseonbin <[email protected]>
1 parent 9a3563f commit 21403d7

39 files changed

+1681
-518
lines changed

build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ dependencies {
4040
testAnnotationProcessor 'org.projectlombok:lombok'
4141
testImplementation 'org.springframework.boot:spring-boot-starter-test'
4242
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
43-
43+
// 인메모리 캐시
44+
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.6'
4445
// jwt
4546
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
4647
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
@@ -78,6 +79,7 @@ dependencies {
7879
// db migration
7980
implementation 'org.flywaydb:flyway-core'
8081
implementation 'org.flywaydb:flyway-mysql'
82+
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
8183

8284
}
8385

src/main/java/sevenstar/marineleisure/MarineLeisureApplication.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
55
import org.springframework.boot.context.properties.EnableConfigurationProperties;
6-
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
6+
import org.springframework.cache.annotation.EnableCaching;
7+
import org.springframework.scheduling.annotation.EnableAsync;
78

89
import sevenstar.marineleisure.global.api.config.properties.KhoaProperties;
910
import sevenstar.marineleisure.global.api.config.properties.OpenMeteoProperties;
10-
import sevenstar.marineleisure.global.api.config.properties.OpenMeteoProperties;
1111

1212
@SpringBootApplication
1313
@EnableConfigurationProperties({KhoaProperties.class, OpenMeteoProperties.class})
14+
@EnableAsync
15+
@EnableCaching
1416
public class MarineLeisureApplication {
1517

1618
public static void main(String[] args) {

src/main/java/sevenstar/marineleisure/global/api/kakao/service/PresetSchedulerService.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import java.time.LocalDate;
44

5+
import org.springframework.cache.annotation.CacheEvict;
56
import org.springframework.stereotype.Service;
7+
import org.springframework.transaction.annotation.Transactional;
68

79
import lombok.RequiredArgsConstructor;
810
import sevenstar.marineleisure.global.enums.Region;
@@ -18,10 +20,12 @@ public class PresetSchedulerService {
1820
private final OutdoorSpotRepository outdoorSpotRepository;
1921
private final SpotPresetRepository spotPresetRepository;
2022

23+
@Transactional
2124
public void updateRegionApi() {
2225
LocalDate now = LocalDate.now();
2326
BestSpot emptySpot = new BestSpot(-1L, "없는 지역입니다", TotalIndex.NONE);
2427
for (Region region : Region.getAllKoreaRegion()) {
28+
evictRegionCache(region);
2529
BestSpot bestSpotInFishing = outdoorSpotRepository.findBestSpotInFishing(region.getLatitude(),
2630
region.getLongitude(), now, PRESET_RADIUS).map(BestSpot::new).orElse(emptySpot);
2731
BestSpot bestSpotInMudflat = outdoorSpotRepository.findBestSpotInMudflat(region.getLatitude(),
@@ -38,4 +42,9 @@ public void updateRegionApi() {
3842
bestSpotInSurfing.getTotalIndex().name());
3943
}
4044
}
45+
46+
@CacheEvict(value = "spotPresetPreviews", key = "#region.name()")
47+
public void evictRegionCache(Region region) {
48+
// 아무 동작 없음
49+
}
4150
}
Lines changed: 5 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,24 @@
11
package sevenstar.marineleisure.global.api.openmeteo.dto.service;
22

33
import java.time.LocalDate;
4-
import java.time.LocalDateTime;
4+
import java.util.List;
55

6-
import org.springframework.core.ParameterizedTypeReference;
7-
import org.springframework.http.ResponseEntity;
86
import org.springframework.stereotype.Service;
97
import org.springframework.transaction.annotation.Transactional;
108

119
import lombok.RequiredArgsConstructor;
12-
import sevenstar.marineleisure.forecast.repository.FishingRepository;
13-
import sevenstar.marineleisure.forecast.repository.MudflatRepository;
14-
import sevenstar.marineleisure.forecast.repository.ScubaRepository;
15-
import sevenstar.marineleisure.forecast.repository.SurfingRepository;
16-
import sevenstar.marineleisure.global.api.openmeteo.OpenMeteoApiClient;
17-
import sevenstar.marineleisure.global.api.openmeteo.dto.common.OpenMeteoReadResponse;
18-
import sevenstar.marineleisure.global.api.openmeteo.dto.item.SunTimeItem;
19-
import sevenstar.marineleisure.global.api.openmeteo.dto.item.UvIndexItem;
20-
import sevenstar.marineleisure.spot.domain.OutdoorSpot;
21-
import sevenstar.marineleisure.spot.repository.OutdoorSpotRepository;
10+
import sevenstar.marineleisure.spot.dto.detail.provider.ActivityProvider;
2211

2312
@Service
2413
@RequiredArgsConstructor
25-
@Transactional(readOnly = true)
2614
public class OpenMeteoService {
27-
private final OpenMeteoApiClient openMeteoApiClient;
28-
private final OutdoorSpotRepository outdoorSpotRepository;
29-
private final FishingRepository fishingRepository;
30-
private final MudflatRepository mudflatRepository;
31-
private final ScubaRepository scubaRepository;
32-
private final SurfingRepository surfingRepository;
15+
private final List<ActivityProvider> providers;
3316

34-
// TODO : exception , refactoring
3517
@Transactional
3618
public void updateApi(LocalDate startDate, LocalDate endDate) {
37-
// update fishing uvIndex
38-
for (Long spotId : fishingRepository.findByForecastDateBetween(startDate, endDate)) {
39-
OutdoorSpot outdoorSpot = outdoorSpotRepository.findById(spotId).orElseThrow();
40-
UvIndexItem uvIndex = getUvIndex(startDate, endDate, outdoorSpot.getLatitude().doubleValue(),
41-
outdoorSpot.getLongitude().doubleValue());
42-
for (int i = 0; i < uvIndex.getTime().size(); i++) {
43-
Float uvIndexValue = uvIndex.getUvIndexMax().get(i);
44-
LocalDate date = uvIndex.getTime().get(i);
45-
fishingRepository.updateUvIndex(uvIndexValue, spotId, date);
46-
}
19+
for (ActivityProvider provider : providers) {
20+
provider.update(startDate, endDate);
4721
}
48-
49-
// update mudflat uvIndex
50-
for (Long spotId : mudflatRepository.findByForecastDateBetween(startDate, endDate)) {
51-
OutdoorSpot outdoorSpot = outdoorSpotRepository.findById(spotId).orElseThrow();
52-
UvIndexItem uvIndex = getUvIndex(startDate, endDate, outdoorSpot.getLatitude().doubleValue(),
53-
outdoorSpot.getLongitude().doubleValue());
54-
for (int i = 0; i < uvIndex.getTime().size(); i++) {
55-
Float uvIndexValue = uvIndex.getUvIndexMax().get(i);
56-
LocalDate date = uvIndex.getTime().get(i);
57-
mudflatRepository.updateUvIndex(uvIndexValue, spotId, date);
58-
}
59-
}
60-
61-
// update scuba sunrise and sunset
62-
for (Long spotId : scubaRepository.findByForecastDateBetween(startDate, endDate)) {
63-
OutdoorSpot outdoorSpot = outdoorSpotRepository.findById(spotId).orElseThrow();
64-
SunTimeItem sunTimeItem = getSunTimes(startDate, endDate, outdoorSpot.getLatitude().doubleValue(),
65-
outdoorSpot.getLongitude().doubleValue());
66-
for (int i = 0; i < sunTimeItem.getTime().size(); i++) {
67-
LocalDateTime sunrise = sunTimeItem.getSunrise().get(i);
68-
LocalDateTime sunset = sunTimeItem.getSunset().get(i);
69-
LocalDate date = sunTimeItem.getTime().get(i);
70-
scubaRepository.updateSunriseAndSunset(sunrise.toLocalTime(), sunset.toLocalTime(), spotId, date);
71-
}
72-
}
73-
74-
// update surfing uvIndex
75-
for (Long spotId : surfingRepository.findByForecastDateBetween(startDate, endDate)) {
76-
OutdoorSpot outdoorSpot = outdoorSpotRepository.findById(spotId).orElseThrow();
77-
UvIndexItem uvIndex = getUvIndex(startDate, endDate, outdoorSpot.getLatitude().doubleValue(),
78-
outdoorSpot.getLongitude().doubleValue());
79-
for (int i = 0; i < uvIndex.getTime().size(); i++) {
80-
Float uvIndexValue = uvIndex.getUvIndexMax().get(i);
81-
LocalDate date = uvIndex.getTime().get(i);
82-
surfingRepository.updateUvIndex(uvIndexValue, spotId, date);
83-
}
84-
}
85-
8622
}
8723

88-
private SunTimeItem getSunTimes(LocalDate startDate, LocalDate endDate, double latitude, double longitude) {
89-
ResponseEntity<OpenMeteoReadResponse<SunTimeItem>> response = openMeteoApiClient.getSunTimes(
90-
new ParameterizedTypeReference<>() {
91-
}, startDate, endDate, latitude, longitude);
92-
return response.getBody().getDaily();
93-
}
94-
95-
private UvIndexItem getUvIndex(LocalDate startDate, LocalDate endDate, double latitude, double longitude) {
96-
ResponseEntity<OpenMeteoReadResponse<UvIndexItem>> response = openMeteoApiClient.getUvIndex(
97-
new ParameterizedTypeReference<>() {
98-
}, startDate, endDate, latitude, longitude);
99-
return response.getBody().getDaily();
100-
}
10124
}
Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package sevenstar.marineleisure.global.api.scheduler;
22

33
import java.time.LocalDate;
4+
import java.util.concurrent.CompletableFuture;
5+
import java.util.concurrent.Executor;
46

57
import org.springframework.scheduling.annotation.Scheduled;
68
import org.springframework.stereotype.Service;
7-
import org.springframework.transaction.annotation.Transactional;
89

910
import lombok.RequiredArgsConstructor;
1011
import lombok.extern.slf4j.Slf4j;
@@ -14,29 +15,59 @@
1415
import sevenstar.marineleisure.spot.repository.SpotViewQuartileRepository;
1516

1617
@Service
17-
@RequiredArgsConstructor
18-
@Transactional(readOnly = true)
1918
@Slf4j
19+
@RequiredArgsConstructor
2020
public class SchedulerService {
2121
public static final int MAX_UPDATE_DAY = 3;
2222
private final KhoaApiService khoaApiService;
2323
private final OpenMeteoService openMeteoService;
2424
private final PresetSchedulerService presetSchedulerService;
2525
private final SpotViewQuartileRepository spotViewQuartileRepository;
2626

27+
28+
private final Executor taskExecutor;
29+
30+
// public SchedulerService(
31+
// KhoaApiService khoaApiService,
32+
// OpenMeteoService openMeteoService,
33+
// PresetSchedulerService presetSchedulerService,
34+
// SpotViewQuartileRepository spotViewQuartileRepository,
35+
// @Qualifier("applicationTaskExecutor") Executor taskExecutor // ★ 여기
36+
// ) {
37+
// this.khoaApiService = khoaApiService;
38+
// this.openMeteoService = openMeteoService;
39+
// this.presetSchedulerService = presetSchedulerService;
40+
// this.spotViewQuartileRepository = spotViewQuartileRepository;
41+
// this.taskExecutor = taskExecutor;
42+
// }
2743
/**
2844
* 앞으로의 스케줄링 전략에 의해 수정될 부분입니다.
2945
* @author guwnoong
3046
*/
3147
@Scheduled(initialDelay = 0, fixedDelay = 86400000)
32-
@Transactional
3348
public void scheduler() {
3449
LocalDate today = LocalDate.now();
3550
LocalDate endDate = today.plusDays(MAX_UPDATE_DAY);
51+
52+
// 1. khoaApiService 먼저 실행 (순차적)
3653
khoaApiService.updateApi(today, endDate);
37-
openMeteoService.updateApi(today, endDate);
38-
presetSchedulerService.updateRegionApi();
39-
spotViewQuartileRepository.upsertQuartile();
54+
55+
// 2. 나머지 작업들을 병렬로 실행
56+
CompletableFuture<Void> openMeteoFuture = CompletableFuture.runAsync(() -> {
57+
openMeteoService.updateApi(today, endDate);
58+
}, taskExecutor);
59+
60+
CompletableFuture<Void> presetSchedulerFuture = CompletableFuture.runAsync(() -> {
61+
presetSchedulerService.updateRegionApi();
62+
}, taskExecutor);
63+
64+
CompletableFuture<Void> spotViewQuartileFuture = CompletableFuture.runAsync(() -> {
65+
spotViewQuartileRepository.upsertQuartile();
66+
}, taskExecutor);
67+
68+
// 모든 병렬 작업이 완료될 때까지 기다림
69+
CompletableFuture.allOf(openMeteoFuture, presetSchedulerFuture, spotViewQuartileFuture).join();
70+
4071
log.info("=== update data ===");
4172
}
4273
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package sevenstar.marineleisure.global.config;
2+
3+
import java.util.concurrent.Executor;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.scheduling.annotation.EnableAsync;
7+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
8+
9+
@Configuration
10+
@EnableAsync
11+
public class AsyncConfig {
12+
13+
@Bean(name = "taskExecutor")
14+
public Executor taskExecutor() {
15+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
16+
executor.setCorePoolSize(2); // 기본 스레드 수
17+
executor.setMaxPoolSize(4); // 최대 스레드 수
18+
executor.setQueueCapacity(100); // 큐 용량
19+
executor.setThreadNamePrefix("Async-Task-With-Sevenball-");
20+
executor.initialize();
21+
return executor;
22+
}
23+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package sevenstar.marineleisure.global.util;
2+
3+
import java.security.MessageDigest;
4+
import java.security.SecureRandom;
5+
import java.util.Base64;
6+
7+
import org.springframework.stereotype.Component;
8+
9+
@Component
10+
public class PkceUtil {
11+
public String generateCodeVerifier() {
12+
SecureRandom random = new SecureRandom();
13+
byte[] bytes = new byte[64];
14+
random.nextBytes(bytes);
15+
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
16+
}
17+
18+
public String generateCodeChallenge(String codeVerifier) {
19+
try {
20+
MessageDigest digest = MessageDigest.getInstance("SHA-256");
21+
byte[] bytes = digest.digest(codeVerifier.getBytes());
22+
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
23+
} catch (Exception e) {
24+
throw new RuntimeException("Failed to generate code challenge", e);
25+
}
26+
}
27+
28+
public boolean verifyCodeChallenge(String codeChallenge, String codeVerifier) {
29+
return false;
30+
}
31+
}

src/main/java/sevenstar/marineleisure/global/util/StateEncryptionUtil.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public String encryptState(String state) {
3939
}
4040
}
4141

42+
4243
/**
4344
* 암호화된 상태 값을 복호화.
4445
*
@@ -67,8 +68,7 @@ public String decryptState(String encryptedState) {
6768
*/
6869
public boolean validateState(String state, String encryptedState) {
6970
try {
70-
String decryptedState = decryptState(encryptedState);
71-
return decryptedState.equals(state);
71+
return decryptState(encryptedState).equals(state);
7272
} catch (Exception e) {
7373
return false;
7474
}
@@ -87,4 +87,4 @@ private SecretKeySpec generateKey(String key) throws NoSuchAlgorithmException {
8787
keyBytes = Arrays.copyOf(keyBytes, 16); // AES-128 키 길이
8888
return new SecretKeySpec(keyBytes, "AES");
8989
}
90-
}
90+
}

0 commit comments

Comments
 (0)