Skip to content

Commit

Permalink
[RELEASE] release: 2차 MVP 일부 및 핫픽스 1.1.0 릴리즈 (#114)
Browse files Browse the repository at this point in the history
* docs: add new properties to application local template

* fix: Fix broken integration test of CalendarApiTest due to changed FamilyApi (#96)

* [OING-126] test: 회원 정보수정/조회/탈퇴에 대한 단위 테스트 코드 추가 (#89)

* [OING-18] feat: 공통 모듈 구축하기 (#5)

* chore: add common module

* chore: add common module to workflow

* feat: add common exception classes

* feat: alter DomainException to non abstract class

* test: add DomainException unit test

* test: add ErrorResponse unit test

* chore: fix workflow annotations

* [OINT-19] feature: 스프링 시큐리티 설정 (#6)

* chore: change root project name

* feat: add SpringSecurityConfig

* feat: add auth handler filters

* feat: add JwtAuthenticationHandler with properties

* feat: add WebRequestInterceptor with filters

* chore: change root project name

* feat: add SpringSecurityConfig

* feat: add auth handler filters

* feat: add JwtAuthenticationHandler with properties

* feat: add WebRequestInterceptor with filters

* feat: change response to ErrorResponse

* feat: add WebExceptionHandler

* style: change line length under max line length

* style: change line length under max line length

* style: match line convention

* [OING-10] chore: code coverage를 위한 Jacoco 설정 (#7)

* chore: apply Jacoco for code coverage

* chore: exclude specific patterns from Jacoco code coverage

* chore: set required formats

* fix: Jacoco configuration by moving jacoco afterEvaluate

* [OING-23] chore: Jacoco 테스트 커버리지 범위에서 config 모듈 제외 (#8)

* [OING-24] chore: Swagger 환경 세팅 (#10)

* feat: add SwaggerConfig

* feat: add SwaggerConfig environment variable

* feat: exclude swagger-related requests from Interceptor path patterns

* style: change swagger title

* [OING-20] feat: JWT 토큰 관리 컴포넌트 생성 (#9)

* feat: add token properties classes

* feat: add token related domain classes

* feat: add token generator and impl

* feat: implement token generator with jwt

* feat: add JWT Authenticator

* test: add unit test for created classes

* [OING-26] chore: CI/CD 파이프라인 워크플로우 개선 (#15)

* [OING-27] chore: 배포 파이프라인 이후 슬랙 알림 보내기

* [OING-27] chore: 배포8] chore: PR시 테스트 커버리지 ì측정하기

* [OING-31] fix: SpringDocs (Swagger) 수리하기 (#18)

* fix: config fix for swagger

* docs: add default url path for swagger

* [OING-25] chore: NCP ObjectStorage 세팅 (#16)

* chore: add dependency for AWS S3

* chore: add NCP ObjectStorage Config

* feat: add File uploader

* test: add application.yaml for test

* test: add test code for uploadImage

* refactor: refact ObjectStorageConfig code

* refactor: change FileUploader for presignedUrl

* feat: add S3PreSignedUrlProvider

* test: add test code for presigned-url

* style: delete unused import statement

* delete: delete unused util file for test

* chore: exclude dto from jacoco test coverage

* style: delete redundant code

* refactor: refact ObjectStorageProperties file

* refactor: refact S3PreSignedUrlProvider code

* delete: delete unused file

* [OING-21] feat: 기본 oAuth & 멤버 인증 관련 모듈 추가 (#17)

* feat: create "POST /v1/auth/social" api

* feat: create interface of apple login service

* feat: create BaseAuditEntity

* feat: add IdentityGenerator and impl

* feat: add Member and SocialMember entity

* feat: add Member Register logic

* feat: add apple provider

* feat: report slack on error

* feat: only invoke slack on production

* chore: add slack webhook conf with local props

* feat: refactor yaml properties

* docs: add environment example

* feat: create "POST /v1/auth/social" api

* feat: create interface of apple login service

* feat: create BaseAuditEntity

* feat: add IdentityGenerator and impl

* feat: add Member and SocialMember entity

* feat: add Member Register logic

* feat: add apple provider

* feat: report slack on error

* feat: only invoke slack on production

* chore: add slack webhook conf with local props

* feat: refactor yaml properties

* docs: add environment example

* test: create test profile

* test: add unit test on domain module

* test: add object unit test codes

* test: add component test codes

* fix: override equals hashcode to check array

* fix: override tostring

* fix: remove setter for entity

* fix: not-null on BaseAuditEntity

* refactor: refactor codes

* test: remove setter test for entity

* [OING-46] feat: Member, Post 모듈 추가하기 (#20)

* feat: add family, post module

* chore: add workflow to check module changes

* [OING-32] refactor: S3 PresignedUrl 로직 리팩터링 (#19)

* refactor: refact PreSignedUrlResponse

* feat: add PreSignedUrlGenerator Interface

* fix: fix getPreSignedUrl Test

* chore: exclude dto from jacoco test coverage

* refactor: delete PreSignedUrlResponse DTO from config

* chore: add NCP ev to template file

* docs: update environment variables on README.md

* chore: fix Qdomains in jacocoTestReport

* feat: delete Qdomain

* feat: document PreSignedUrlResponse

* chore: add dependsOn test in jacocoTestReport

* [OING-50] refactor: Config 모듈명 Gateway로 변경 (#21)

* [OING-33] feat: DDL 작성 및 flyway 활성화 (#24)

* feat: add sql DDL script

* feat: enable flyway

* fix: change url datatype to TEXT

* feat: change emoji to ascii

* test: fix s3 test

* [OING-54] chore: mysql 환경변수 설정 및 프로덕션 환경설정 (#25)

* chore: add mysql password to env

* chore: add hikari configurations

* chore: disable springdoc in production

* chore: add application-dev profile

* chore: change ddl-auto to validate

* chore: test profile to apply h2

* [OING-57] feat: Member 도메인 entity & repository 구성 (#26)

* feat: add entitites for member domain

* feat: change BaseAuditEntity to extend BaseEntity

* [OING-57] hotfix: Member 도메인 entity & repository 구성 오타 (#27)

* [OING-47] feat: Auth 리프레시 API 추가및 경로 변경 (#22)

* docs: add more description and example to api docs

* feat: add refresh token logic

* feat: add refresh token validation logic

* style: fixed line style

* test: add test code to new dto

* [OING-32] refactor: S3 PresignedUrl 로직 리팩터링 (#19)

* refactor: refact PreSignedUrlResponse

* feat: add PreSignedUrlGenerator Interface

* fix: fix getPreSignedUrl Test

* chore: exclude dto from jacoco test coverage

* refactor: delete PreSignedUrlResponse DTO from config

* chore: add NCP ev to template file

* docs: update environment variables on README.md

* chore: fix Qdomains in jacocoTestReport

* feat: delete Qdomain

* feat: document PreSignedUrlResponse

* chore: add dependsOn test in jacocoTestReport

* [OING-50] refactor: Config 모듈명 Gateway로 변경 (#21)

* docs: add more description and example to api docs

* feat: add refresh token logic

* feat: add refresh token validation logic

* style: fixed line style

* test: add test code to new dto

* feat: add new temporary token

* feat: add temporary token creation logic

* feat: add register flow logic

* feat: change api route to /register

* refactor: add custom fromString on enum classes

---------

Co-authored-by: Jisu Lim <[email protected]>
Co-authored-by: sckwon770 <[email protected]>

* [OING-40] feat: Feed부분 API 설계하기 (#23)

* feat: add Pagination Response and DTO

* test: add dto, response test code

* feat: add PostFeedController

* fix: change package issue

* feat: implement mock logic

* test: fix test

* test: add test to PostFeedResponse

* feat: add Member API

* [OING-58] feat: MemberPost 도메인 entity & repository 구성 (#28)

* feat: add MemberPost Entity

* feat: add MemberPostReaction Entity

* feat: add MemberPostComment Entity

* feat: add Member module dependency

* feat: add fetchType to Entity field

* feat: add Repository in post module

* chore: delete member dependency in post module

* test: add domain test in MemberPost module

* feat: add index in post table

* fix: fix mappedBy in MemberPost

* test: fix test error in MemberTest

* fix: fix field in MemberPost domain

* [OING-40] feature: API 경로 변경 및 Response DTO 변경 (#33)

* feat: change api route and controller name

* docs: update api docs

* feat: add hasNext to PaginationResponse

* [OING-62] feat: 가족 구성원 Profile 조회 API 설계 (#29)

* feat: add FamilyMemberProfileResponse DTO

* feat: add getFamilyMemberProfile API

* test: add FamilyMemberProfileResponseTest

* refactor: move getFamilyMemberProfile to member module

* refactor: move FamilyMemberProfileResponse to member module

* style: delete unused file

* feat: add Pagination Response and DTO

* refactor: cherry-pick PaginationDTO

---------

Co-authored-by: ChuYong <[email protected]>

* [Suggestion] chore: PR 작성 자동화 (#32)

* chore: Add the workflos to automate the pointless writing pr template works

* chore: Add last empty line into workflow, pr.yaml

* chore: Add base-branch-regex

* fix: Fix base-branch-regex to constraint branch condition

* [OING-56] feat: Family 모듈 구성 (#31)

* [OING-41] feat: 금일 피드 업로드 여부 조회 API 추가 (#30)

* feat: Add the mock api - getIsTodayFeedUploadedByUserId

* fix: Fix wrong parameter annotation on getIsTodayFeedUploadedByUserId

* refactor: Refactor fetchDailyFeeds api whose scope is ME according to code review

* refactor: Refactor make mock api to response variously

* feat: Add AuthenticationHolder

* chore: Add the comment using TokenAuthenticationHolder

* chore: Exclude QueryDSL Qdomains from git

* chore: Organize module directory structure

* fix: Fix compile error at MemberController

* chore: Exclude .env file from git

* [OING-66] feat: PresignedUrl 요청 API 구현 (#35)

* feat: add S3presignedUrl API for image upload

* refactor: add RequiredArgsConstructor in PostController

* style: modify test code with error

* refactor: refact PresignedUrl logic

* fix: fix requestPresignedUrl http method

* style: rename requestPresignedUrl uri

* [OING-63] feat: 1차 와이어프레임 API 설계 (#34)

* feat: add family api spec

* feat: add member api sepc

* feat: add post create logic

* feat: add calendar query api

* feat: add post query api

* feat: add temporary token generate api

* feat: change api url

* feat: add reaction deletion api

* hotfix: remove unused import

* [OING-73] feat: family, post api mock 구현 (#37)

* feat: mock family api

* feat: mock post api

* feat: add sort parameter

* docs: add description

* hotfix: add missing parameter

* [OING-71] feat: 회원정보 수정 API 모킹 (#38)

* feat: add updateMember mock api

* feat: add updateMember mock api

* [OING-67] feat: 피드 업로드 API 모킹 (#36)

* feat: add V2__modify_Post DDL script

* feat: modify column in Post domain

* feat: add createPost mock API

* test: fix failed test due to added column

* refactor: refact S3 presignedUrl Api

* refactor: delete unused memberId param in requestPresignedUrl

* refactor: change ResponseEntity in requestPresignedUrl

* refactor: refact Postmodule code

* refactor: refact S3PreSignedUrlProvider

* test: fix S3PreSignedUrlProviderTest code

* style: change ResponseEntity to DTO

* feat: correct merge conflicts

* feat: add validation for CreatePostRequest

* refact: add validateContent method

---------

Co-authored-by: 송영민 (YeongMin Song) <[email protected]>

* [OING-68] feat: Post Reaction API 모킹 (#40)

* feat: add PostReaction mock API

* feat: change PostReaction DTO name

* feat: change reactToPost API name

* [OING-83] 회원 탈퇴 API 모킹 (#41)

* feat: add deleteMember mock api

* feat: delete DeleteMemberRequest dto

* feat: change deleteMember mock api logic

* style: delete /v1/members uri in url-whitelists

* [OING-88] feat: Emoji Enum 타입으로 변경 (#43)

* feat: add Emoji Enum Class

* fix: fix emoji logic

* test: fix test due to EnumType

* [OING-84] feat: Widget API 구현 (#42)

* feat: Add OptimizedImageUrlProvider component at gateway

* feat: Add the code of service and repository for calendar controller

* feat: Move Calendar controller to gateway modules and Impl getMonthlyCalendar request

* refactor: Refactor MemberPostRepository to make method name simply rather than giving meaning in detail

* feat: Impl getWeeklyCalendar requst and Substract the dupicated code lines from calendar requests

* fix: Remove the code to substract 1 day from end date at calenar request, considering between operator

* chore: Fix head branch regex for pr template automation workflow

* feat: Add api spec for the single recent family widget

* refactor: Exclude family concept from MemberPostService, MemberPostRepository

* feat: Impl getSingleRecentFamilyPostWidget request

* refactor: Rename memberId to myId

* refactor: Change the type of week paramter of getWeeklyCalendar request to Long

* refactor: Make the date parameters of CalendarCotroller required true

* chore: Add test coverage excluding pattern **.DTO*

* fix: Add cdn value  at test application yml

* refactor: Refactor for loop code to map post to calendar to functional code

* refactor: Break the line over 120 characters

* refactor: Refactor the complex JPQL code of MemberPostRepository to QueryDSL

* refactor: Change the type of paramters of MemberPost code

* refactor: Remove implementation class usage, not interface of OptimizedImageUrl component

* [OING-70] feat: Calendar API 구현 (#39)

* feat: Add OptimizedImageUrlProvider component at gateway

* feat: Add the code of service and repository for calendar controller

* feat: Move Calendar controller to gateway modules and Impl getMonthlyCalendar request

* refactor: Refactor MemberPostRepository to make method name simply rather than giving meaning in detail

* feat: Impl getWeeklyCalendar requst and Substract the dupicated code lines from calendar requests

* fix: Remove the code to substract 1 day from end date at calenar request, considering between operator

* chore: Fix head branch regex for pr template automation workflow

* refactor: Exclude family concept from MemberPostService, MemberPostRepository

* refactor: Rename memberId to myId

* refactor: Change the type of week paramter of getWeeklyCalendar request to Long

* refactor: Make the date parameters of CalendarCotroller required true

* chore: Add test coverage excluding pattern **.DTO*

* fix: Add cdn value  at test application yml

* refactor: Refactor for loop code to map post to calendar to functional code

* refactor: Break the line over 120 characters

* refactor: Refactor the complex JPQL code of MemberPostRepository to QueryDSL

* refactor: Change the type of paramters of MemberPost code

* refactor: Remove implementation class usage, not interface of OptimizedImageUrl component

---------

Co-authored-by: 송영민 (YeongMin Song) <[email protected]>

* [OING-90] hotfix: MemberService 및 WidgetController에서의 컴파일 에러 (#44)

* hotfix: update swagger header

* hotfix: add token security

* [OING-75] feat: Post 작성 API 구현 (#45)

* [OING-79] feat: 멤버 닉네임/프로필 이미지 수정 API 구현 (#47)

* feat: add updateMember API

* feat: add requestPresignedUrl for profileImg API

* feat: add transactional annotation

* style: remove getMember method

* refactor: split existing logic into two separate APIs

* feat: add deleteMemberProfileImage method

* style: update imageUrl RequestDTO example

* style: change method name for delete ObjectStorage image

* refactor: add validateName logic

* feat: change queryString to requestBody

* feat: move validateName method

* feat: add Async to deleteImage logic

* style: add newline

* [OING-77] feat: Post 조회 관련 API 구현 (#50)

* feat: implement GET v1/posts

* feat: implement search posts

* style: remove unused import

* refactor: change to query-dsl style

* [OING-81] feat: v1/families 관련 API 구현 (#49)

* feat: implement POST /v1/families

* feat: implement GET /v1/families/invitation-link

* feat; change method name

* feat: change link to constant

* feat: change to new exception class

* [OING-80] feat: 멤버 정보 조회 관련 및 가족 그룹 생성일 조회 API 구현 (#46)

* feat: add getFamilyMemberProfile API

* feat: add getMember API

* feat: add familyCreatedAt in FamiyMemberProfilesResponse DTO

* refactor: refact getFamilyMemberProfiles method

* style: code cleanup

* feat: delete family dependency in member

* feat: seperate getFamilyCreatedAt API

* style: change default value in getFamilyMemberProfiles

* style: delete unused annotation

* refactor: refact findFamilyCreatedAt method

* style: rename method for getFamilyMembersProfiles

* refactor: refact createFamilyMemberProfiles logic

* fix: remove sorting for findFamilyMembersProfiles

* style: change getFamilyCreatedAt ResponseDto

* refact: createFamilyMemberProfiles method logic

* [OING-97] CalendarApi와 관련된 에러와 잘못된 설정 해결 (#54)

* fix: Add alias name at memberPost.count() to fix runtime QueryDSL query error

* refactor: Make param require false at CalendarApi

* chore: Add Swagger params description to explain type of CalendarApi

* feat: commenting out deleteMemberProfileImage method (#55)

* [OING-97] PR54로 구현한 티켓의 런타입 에러 해결을 위한 핫픽스... (#56)

* fix: Add alias name at memberPost.count() to fix runtime QueryDSL query error

* refactor: Make param require false at CalendarApi

* chore: Add Swagger params description to explain type of CalendarApi

* fix: Change MemberPostCountDTO type to Class to use the QueryDSL projections field instead of projections bean that occur runtime error

* fix: Add NoArgsContructor at MemberPostCountDTO to fix QueryDSL projections QBean runtime error (#57)

* [OING-76] feat: Post에 대한 반응 생성/삭제 구현 (#48)

* feat: add validate logic for createPostReaction

* feat: add createPostReaction API

* feat: add deletePostReaction API

* refactor: refact post reaction logic in controller

* refactor: refact findReaction logic in service

* fix: change FetchType Eager to Lazy

* [OING-94] fix: createPost 코드 개선 (#51)

* refactor: refact createPost code

* refactor: delete unused log

* feat: add DuplicatePostUploadException

* style: add comments for upload time validation

* refactor: convert time comparison logic to use LocalDateTime

* fix: fix postDate for Asia/Seoul

* refactor: refact PostResponse

* style: detail InvalidUploadTime Exception

* fix: remove extractLocalDate method

* style: correct image request dto example

* fix: fix validateUploadTime logic to ZonedDateTime

* [OING-95] feat: 카카오 인증(로그인) 기능 구현 (#52)

* feat: add kakao provider

* feat: implement kakao

* [OING-92] feat: 회원 탈퇴 API 구현 (#58)

* feat: add BaseAuditEntityWithDelete

* feat: add member delete basic logic

* feat: add findAllSocialMemberByMember method

* fix: fix updateDeletedAt()

* refact: delete updateDeletedAt method argument

* feat: add memberId PathVariable

* refactor: refact deleteMember logic

* feat: add memberId PathVariable in Put method

* fix: fix social login provider test

* [OING-100] feat: 내 정보 조회 API & MemberResponse 누락필드 추가 (#59)

* feat: move member response to function

* feat: add me api

* hotfix: fix page count issue (#60)

* [OING-102] hotfix: Family 초대 링크 기발급 경우 기존 링크 반환 (#61)

* feat: change emoji type name

* feat: implement post reaction api

* [OING-107] 임시 토큰 통한 회원가입 불가 이슈 해결 (#62)

* hotfix: add missing transactional

* [OING-108] feature: 응답값 없는 Operation의 기본 응답값 추가 (#63)

* feat: add memberdevice domain

* feat: add device api

* fix: change missing name

* [OING-109] feat: Member, MemberPost 엔티티에 imageKey 필드 추가 (#64)

* Revert "[OING-109] feat: Member, MemberPost 엔티티에 imageKey 필드 추가 (#64)"

This reverts commit 926f381.

* Revert "Revert "[OING-109] feat: Member, MemberPost 엔티티에 imageKey 필드 추가 (#64)""

This reverts commit 0bb818d.

* [OING-111] 새로운 형태의 Object storage url으로 인해 썸네일 url이 작동하지 못한 오류 해결 #65

* feat: add not found handler

* [OING-112] feat: 딥링크 API & 딥링크 가족 가입 API 구현 (#66)

* feat: add family join api

* feat: add deep link api

* feat: add join family feature

* Revert "feat: add not found handler"

This reverts commit 6bcdaa1.

* feat: handle 404

* [OING-113] refactor: 게시물 반응 전체 조회 응답 형식 수정 (#67)

* [OING-115] feat: 프로덕션용 설정 및 앱 키 기능 추가 (#68)

* feat: fix validation

* feat: config production-ready setups

* feat: add additional logging

* feat: add app version feature

* feat: changed log targets

* feat: change method not allowed exception

* [OING-120] hotfix: 내 가족만 게시물 조회 가능하게 & 앱 키 스웨거 추가 (#69)

* feat: add version filter toggle

* feat: add app version key in swagger

* feat: only query my family

* [OING-118] hotfix: 기회원가입자 재회원가입 방지 (#70)

* test: jwt 헤더 테스트 깨지는거 해결

* feat: prevent register if already member exists

* [OING-119] feat: Post content 공백 존재 검증 로직 추가 (#71)

* feat: add spacing validate logic in post

* style: change throw InvalidParameterException

* test: jwt 헤더 테스트 깨지는거 해결

---------

Co-authored-by: ChuYong <[email protected]>

* [OING-122] hotfix: post 등록 시, 내용 길이 검증 수정 (#73)

* [OING-121] refactor: 1차 MVP 코드 전체 정리 (#72)

* style: clean up code

* refactor: common module package cleanup

* refactor: family module package cleanup

* refactor: member module package cleanup

* refactor: post module package cleanup

* style: clean up code finally

* feat: remove asyncconfig

* refactor: refact post module class name

* [OING-122] hotfix: post 등록 시, 내용 길이 검증 수정 (#73)

* style: clean up code

* refactor: common module package cleanup

* refactor: family module package cleanup

* refactor: member module package cleanup

* refactor: post module package cleanup

* style: clean up code finally

* feat: remove asyncconfig

* refactor: refact post module class name

---------

Co-authored-by: jisu <[email protected]>
Co-authored-by: Jisu Lim <[email protected]>

* [OING-124] hotfix: 리프레시 api 작동 안함 해결 (#75)

* feat: add links to whitelisted url

* fix: change image key extraction logic (#74)

* [OING-125] hotfix: 유효하지 않은 토큰에 대해 401 예외 반환하는 핸들러 추가 (#76)

* hotfix: add handleAuthenticationFailedException

* style: change log message

* [OING-125] hotfix: PR76 핫픽스 (#77)

* hotfix: add handleAuthenticationFailedException

* style: change log message

* fix: change 401 to HttpStatus UNAUTHORIZED

* [OING-125] hotfix: PR76 핫픽스 2 (#78)

* [OING-127] feat: 구글 oAuth 추가 (#79)

* [OING-128] refactor: 가족 멤버 프로필 조회 시, 탈퇴한 회원은 응답에서 제외되도록 수정 (#80)

* feat: add DeletedAtIsNull to findFamilyMembers

* test: fix SocialLoginProviderTest

* style: delete unused lines

* [OING-129] Widget용 이미지 압축 기능 추가 (#82)

* [OING-130] feat: postDate 컬럼 삭제 및 가족이 없는 멤버에 대해 가족 프로필 조회 시, 예외 반환 로직 추가 (#83)

* feat: delete postDate column in post

* style: change method name

* feat: add delete postdate column sql

* fix: fix existsByMemberIdAndCreatedAt method

* test: fix memberPost test

* feat: add FamilyNotFoundException

* [OING-129] OING-129 의 컴파일 에러 핫픽스 (#84)

* feat: Add getKBImageUrlGenerator at OptimizedImageUrlGenerator and Append image optimizing code at getSingleRecentFamilyPostWidget at WidgetController

* fix: Hotfix compile error

* [OING-129] hotfix: OING-129 PR의 핫픽스가 머지되는 중에 꼬인 에러 해결 (#85)

* feat: Add getKBImageUrlGenerator at OptimizedImageUrlGenerator and Append image optimizing code at getSingleRecentFamilyPostWidget at WidgetController

* fix: Hotfix compile error

* fix: Fix wrongly twisted merged getKBImageUrlGenerator code

* chore: update swarm service name

* [OING-129] OING-129 PR에서 이미지 최적화 쿼리를 변경 (#86)

* feat: Add getKBImageUrlGenerator at OptimizedImageUrlGenerator and Append image optimizing code at getSingleRecentFamilyPostWidget at WidgetController

* fix: Hotfix compile error

* fix: Fix wrongly twisted merged getKBImageUrlGenerator code

* refactor: Change KB_IMAGE_OPTIMIZER_QUERY_STRING

* chore: update prod swarm service name

* [OING-131] 위젯 응답 객체에 게시자 이름 추가 (#87)

* test: add MemberControllerTest for change profile info

* test: add MemberControllerTest for get Member Profile info

* test: add MemberControllerTest for memberDelete

* test: add MemberControllerTest for exception and presignedUrl

* [OING-129] 이미지 최적화 빈의 NullPointerException 방지 (#88)

* [OING-104] test: 월간 캘린더 API 통합테스트 추가 (#81)

* feat: Add test code for getMonthlyCalendar of CalendarController

* fix: Fix calendar query that cant group the posts daily and Fix test code according to changed calendar query

* refactor: Change the integration test, CalendarControllerTest to CalendarApiTest

* fix: Add fake token expiration value in applicaiton-test.yaml

* fix: Remove post_date column from meber_post insertion sql at CalenarApiTest

* test: add additional test for nickname validate

* test: add additional test for nickname validate

* fix: fix getThumbnailUrlGenerator typo

* hotfix: fix getThumbnailUrlGenerator typo (#90)

* [OING-135] hotfix: 닉네임 길이 제한 조건 수정 (#92)

* fix: fix validateMemberName condition

* feat: add/fix @Valid for DTO

* feat: add @notblank to CreateNewUserDto

* test: fix Nickname validate test

* test: add MemberControllerTest for change profile info

* test: add MemberControllerTest for get Member Profile info

* test: add MemberControllerTest for memberDelete

* test: add additional test for nickname validate

* test: add additional test for nickname validate

* fix: fix getThumbnailUrlGenerator typo

* hotfix: fix getThumbnailUrlGenerator typo (#90)

* [OING-135] hotfix: 닉네임 길이 제한 조건 수정 (#92)

* fix: fix validateMemberName condition

* feat: add/fix @Valid for DTO

* feat: add @notblank to CreateNewUserDto

* [OING-135] hotfix: 닉네임 길이 제한 조건 수정 (#92)

* fix: fix validateMemberName condition

* feat: add/fix @Valid for DTO

* feat: add @notblank to CreateNewUserDto

* test: fix Nickname validate test

* test: remove spy

---------

Co-authored-by: 송영민 (YeongMin Song) <[email protected]>
Co-authored-by: sckwon770 <[email protected]>

* fix: update filter condition

* [OING-142] feat: 댓글 기능 설계 & 구현 & 테스트 (#97)

* fix: fix generic response return issue

* feat: structure api document

* feat: update dto

* feat: implement crud

* feat: add some validations

* feat: add family validation

* test: add MemberPostCommentControllerTest

* test: add MemberPostCommentControllerTest

* test: add MemberPostCommentApiTest

* feat: add count to member post

* fix: update exception type

* [OING-143] feat: 회원탈퇴 이유 기능 추가 (#98)

* feat: add quit reason feature

* test: add MemberQuitReasonServiceTest

* test: add MemberApiTest with quit

* feat: change type to enum

* feat: allow multi cause

* test: fix test

* feat: add dayOfBirth in FamilyMemberProfileResponse (#103)

* [OING-148] feat: Post 삭제 기능 추가 (#102)

* feat: add DeleteMemberPost Api

* refactor: add EventListener for DeleteMemberPost

* [OING-147] feat: 리얼 이모지 테이블 클래스 및 리얼 이모지 API 틀 추가 (#101)

* chore: add real emoji tbl migration file

* feat: add RealEmoji Entity class

* feat: add RealEmojiApi class

* test: add additional field

* feat: add PostRealEmojiResponse

* feat: add RealEmoji Controller class

* fix: change MemberPostRealEmoji's realEmoji Type

* fix: change MemberPostRealEmoji's realEmoji relationship

* style: change realEmoji dto

* fix: delete BodyRequest in deleteRealEmoji API

* refactor: refact RealEmojiApi uri

* fix: fix comment saving issue

* [OING-144] feat: 가족 탈퇴 API 추가 (#99)

* [OING-105] feat: 캘린더 API를 위한 테스트 추가 (#104)

* feat: Add the integration test for weekly calendarApiTest

* feat: Add edge case of the integration test at CalendarApiTest

* feat: Add unit test for CalendarController

* feat: Add DataJpaTest for MemberPostRepositoryCustom methods for calendar

* refactor: Change variable name from User to Member

* [OING-151] feat: 회원의 리얼이모지 POST/PUT API 구현 (#105)

* feat: add requestPresignedUrl for RealEmoji

* feat: add MemberRealEmoji Post/Put API

* feat: add MemberRealEmoji post/put api test

* feat: add MemberRealEmoji post/put Integration test

* fix: fix MemberRealEmojiControllerTest

* test: add additional unit test code

* style: add line

* docs: README, LICENSE 추가

* fix: fix saved time issue

* [OING-156] refactor: 이미지의 예상되지 않은 url 패턴으로 인한 OptimizedImageProvider의 런타임 예외를 방지 (#108)

* [OING-146] feat: 현재 어플리케이션 최신버전 유무 API 추가 (#100)

* [OING-154] feat: 회원의 리얼이모지 조회 API 구현 (#109)

* test: fix createPostComment test

* feat: add getMemberRealEmoji api

* style: modify description

* test: add getMemberRealEmoji test

* test: add getMemberRealEmoji integration test

* [OING-161] test: 피드 리액션 등록/삭제/남긴 멤버 조회 기능 테스트 코드 작성 (#111)

* test: add MemberPostReactionController unit test

* test: add MemberPostReactionController integration test

* [OING-159] feat: 피드에 등록된 리얼이모지 리스트 조회 API 모킹 (#110)

* [OING-153] feat: 피드 리얼이모지 POST/DELETE API 구현 (#106)

* feat: add createPostRealEmoji Api

* feat: add deletePostRealEmoji Api

* test: add MemberPostRealEmoji unit test

* test: add MemberPostRealEmoji integration test

---------

Co-authored-by: 송영민 (YeongMin Song) <[email protected]>

* [OING-158] feat: 피드 리얼이모지 조회 API 구현 (#113)

* feat: add getMemberPostRealEmoji api

* style: change dto name

* refactor: refact getPostRealEmojiMembers code

* test: add MemberPostRealEmojiController get api unit test

* test: add MemberPostRealEmojiController get api integration test

* [OING-162] test: Post PresignedUrl/POST/DELETE 테스트 코드 추가 (#112)

* test: add MemberPostController test

* test: add MemberPostController integration test

* test: add MemberPostRepository test

---------

Co-authored-by: 송영민 (YeongMin Song) <[email protected]>
Co-authored-by: sckwon770 <[email protected]>
  • Loading branch information
3 people authored Jan 18, 2024
1 parent 9d45a89 commit fda2632
Show file tree
Hide file tree
Showing 106 changed files with 4,535 additions and 93 deletions.
20 changes: 20 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright (c) 2012-2024 Scott Chacon and others

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
54 changes: 51 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,56 @@
## 14th-team-BE
# Bibbi: 하루 한번, 가족에게 보내는 생존 신고

디프만 14기 팀 백엔드 프로젝트입니다!
` 연락에 대한 부담감과 거부감이 들지 않게
간편하고 사용하기 쉬운 기능으로
일상을 공유하게 유도한다 `

### 환경변수

<img src = "https://github.com/depromeet/14th-team5-iOS/assets/62610032/ef7d1e84-93b8-4def-b12a-85dd95c22bce" width = "800" />

<br />


#### "하루 한 번, 가족과의 소중한 연락!"

가족은 삶의 중요한 부분이죠. 하지만 빠른 일상에 묻혀 자주 소통하지 못하는 경우가 많습니다. 이제, 삐삐와 함께 '일일 생존 신고' 프로젝트를 시작해보세요!

매일, 간단한 메세지와 사진을 통해 가족에게 생존을 알리면서 소중한 순간들을 함께 나눌 수 있습니다. 까먹지 않고, 더욱 멋지고 따뜻한 가족 소통의 시작을 만들어보세요. 나중에는 이 작은 노력이 행복한 추억으로 기억될 것입니다.

가족과의 소중한 시간, 삐삐와 함께라면 언제나 더 특별한 것 같아요! ❤️


> "Once a day, cherish the connection with your family!
Family is an essential part of life, yet amidst the fast-paced routine, meaningful communication often takes a back seat. Now, with the 'Daily Survival Report' project by Pippy, initiate a new era of communication with your loved ones!
Every day, through simple messages and photos, you can share your survival with your family, creating moments of togetherness. Never forget, with Pippy, embark on a journey of stylish and warm family communication. Later on, these small efforts will be remembered as joyful memories.
In the precious time spent with family, everything feels more special with Pippy by your side! ❤️"


<br />

### 🎇 Project Contributors

<table>
<tbody>
<tr>
<td align="center" valign="top" width="22.28%"><a href="https://github.com/cchuyong"><img src="https://avatars.githubusercontent.com/u/67673493?v=4" width="140px;" alt="CChuYong"/><br /><sub><b>Yeongmin Song</b></sub></a><br /><span>백엔드 개발(파트장)</span></td>
<td align="center" valign="top" width="22.28%"><a href="https://github.com/cchuyong"><img src="https://avatars.githubusercontent.com/u/69844138?v=4" width="140px;" alt="CChuYong"/><br /><sub><b>Jisoo Lim</b></sub></a><br /><span>백엔드 개발</span></td>
<td align="center" valign="top" width="22.28%"><a href="https://github.com/cchuyong"><img src="https://avatars.githubusercontent.com/u/49567744?v=4" width="140px;" alt="CChuYong"/><br /><sub><b>Soonchan Kwon</b></sub></a><br /><span>백엔드 개발</span></td>
</tr>
</tbody>
</table>

<br/>

### 🖥️ Project Tech Stacks

- JVM Runtime Amazon Corretto 17
- SpringBoot 3.1.5 (Servlet MVC)
- Spring Data JPA with QueryDSL
- Stateless Session Management with JWT + Spring Security
- Module Architecture with Gradle Multi-Project
<br/><br/>

### 🛠 환경변수

| 이름 | 설명 |
|----------------------------|-----------------------------|
Expand Down
2 changes: 0 additions & 2 deletions common/src/main/java/com/oing/dto/response/ArrayResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
* Date: 2023/12/05
* Time: 12:30 PM
*/
@Schema(description = "배열(복수) 응답")
public record ArrayResponse<T>(
@Schema(description = "실제 데이터 컬렉션", example = "[\"data\"]")
Collection<T> results
) {
public static <T> ArrayResponse<T> of(Collection<T> results) {
Expand Down
10 changes: 0 additions & 10 deletions common/src/main/java/com/oing/dto/response/PaginationResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,11 @@
* Date: 2023/12/05
* Time: 12:30 PM
*/
@Schema(description = "페이지네이션 응답")
public record PaginationResponse<T>(
@Schema(description = "현재 페이지", example = "1")
int currentPage,

@Schema(description = "전체 페이지 수", example = "30")
int totalPage,

@Schema(description = "페이지당 데이터 수", example = "10")
int itemPerPage,

@Schema(description = "더 데이터가 있는지", example = "true")
boolean hasNext,

@Schema(description = "실제 데이터 컬렉션", example = "[\"data\"]")
Collection<T> results
) {
public static <T> PaginationResponse<T> of(PaginationDTO<T> dto, int currentPage, int itemPerPage) {
Expand Down
11 changes: 11 additions & 0 deletions common/src/main/java/com/oing/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ public enum ErrorCode {
*/
EMOJI_ALREADY_EXISTS("EM0001", "Emoji already exists"),
EMOJI_NOT_FOUND("EM0002", "Emoji not found"),
/**
* MemberComment Related Errors
*/
POST_COMMENT_NOT_FOUND("CM0001", "Comment not found"),
/**
* Family Related Errors
*/
Expand All @@ -50,6 +54,13 @@ public enum ErrorCode {
INVALID_UPLOAD_TIME("PO0001", "Invalid Upload Time. The request is outside the valid time range" +
"(from 12:00 AM yesterday to 12:00 AM today)."),
DUPLICATE_POST_UPLOAD("PO0002", "Duplicate Post Upload"),
/**
* Real-Emoji Related Errors
*/
REAL_EMOJI_NOT_FOUND("RE0001", "Real-Emoji not found"),
REAL_EMOJI_ALREADY_EXISTS("RE0002", "Real-Emoji already exists"),
REGISTERED_REAL_EMOJI_NOT_FOUND("RE0003", "Registered Real-Emoji not found"),
DUPLICATE_REAL_EMOJI("RE0004", "Duplicate Real Emoji"),
/**
* Deep Link Related Errors
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.oing.exception;

public class StringEmptyWhiteSpaceException extends RuntimeException {
public StringEmptyWhiteSpaceException() {
super();
}
}
9 changes: 9 additions & 0 deletions common/src/main/java/com/oing/service/MemberBridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,13 @@ public interface MemberBridge {
* @return family id
*/
String getFamilyIdByMemberId(String memberId);

/**
* 같은 가족에 속해있는지 확인합니다
* @param memberIdFirst 첫 번쨰 사용자 아이디
* @param memberIdSecond 두 번째 사용자 아이디
* @return 가족 같은지 여부 (한쪽이라도 null이면 false)
* @throws com.oing.exception.MemberNotFoundException 사용자가 존재하지 않을 경우
*/
boolean isInSameFamily(String memberIdFirst, String memberIdSecond);
}
2 changes: 2 additions & 0 deletions common/src/main/java/com/oing/util/PreSignedUrlGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public interface PreSignedUrlGenerator {

PreSignedUrlResponse getProfileImagePreSignedUrl(String imageName);

PreSignedUrlResponse getRealEmojiPreSignedUrl(String imageName);

String extractImageKey(String imageUrl);
}
19 changes: 19 additions & 0 deletions common/src/test/java/com/oing/QueryDslTestConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.oing;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;

@TestConfiguration
public class QueryDslTestConfig {

@PersistenceContext
private EntityManager entityManager;

@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
4 changes: 4 additions & 0 deletions gateway/src/main/java/com/oing/component/AppVersionCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ public boolean isServiceable(UUID appKey) {
AppVersion appVersion = appVersionMap.get(appKey);
return appVersion != null && appVersion.isInService();
}

public AppVersion getAppVersion(UUID appKey) {
return appVersionMap.get(appKey);
}
}
10 changes: 10 additions & 0 deletions gateway/src/main/java/com/oing/config/SpringWebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.oing.config.filter.WebRequestInterceptor;
import com.oing.config.support.AppKeyResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Collections;
import java.util.List;

/**
* no5ing-server
Expand All @@ -23,6 +26,8 @@
@Configuration
public class SpringWebConfig implements WebMvcConfigurer {
final WebRequestInterceptor webRequestInterceptor;
final AppKeyResolver appKeyResolver;

@Value("${app.oauth.google-client-id}")
private String googleClientId;

Expand All @@ -37,4 +42,9 @@ public GoogleIdTokenVerifier googleIdTokenVerifier() {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webRequestInterceptor);
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(appKeyResolver);
}
}
31 changes: 31 additions & 0 deletions gateway/src/main/java/com/oing/config/support/AppKeyResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.oing.config.support;

import com.google.common.base.Preconditions;
import com.oing.config.properties.WebProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.util.UUID;

@RequiredArgsConstructor
@Component
public class AppKeyResolver implements HandlerMethodArgumentResolver {
private final WebProperties webProperties;

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(RequestAppKey.class) != null;
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String appKey = webRequest.getHeader(webProperties.headerNames().appKeyHeader());
Preconditions.checkNotNull(appKey, "App key is null");
return UUID.fromString(appKey);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.oing.config.support;

import com.oing.exception.StringEmptyWhiteSpaceException;
import com.oing.util.OptimizedImageUrlGenerator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -31,12 +32,17 @@ public class OptimizedImageUrlProvider implements OptimizedImageUrlGenerator {
*/
@Override
public String getThumbnailUrlGenerator(String bucketImageUrl) {
if (bucketImageUrl == null) {
try {
validateUrlEmptyOrWhiteSpace(bucketImageUrl);

String imagePath = bucketImageUrl.substring(bucketImageUrl.indexOf("/images"));
return imageOptimizerCdnUrl + imagePath + THUMBNAIL_OPTIMIZER_QUERY_STRING;

} catch (StringEmptyWhiteSpaceException e) {
return null;
} catch (IndexOutOfBoundsException e) {
return bucketImageUrl;
}

String imagePath = bucketImageUrl.substring(bucketImageUrl.indexOf("/images"));
return imageOptimizerCdnUrl + imagePath + THUMBNAIL_OPTIMIZER_QUERY_STRING;
}


Expand All @@ -47,11 +53,22 @@ public String getThumbnailUrlGenerator(String bucketImageUrl) {
*/
@Override
public String getKBImageUrlGenerator(String bucketImageUrl) {
if (bucketImageUrl == null) {
try {
validateUrlEmptyOrWhiteSpace(bucketImageUrl);

String imagePath = bucketImageUrl.substring(bucketImageUrl.indexOf("/images"));
return imageOptimizerCdnUrl + imagePath + KB_IMAGE_OPTIMIZER_QUERY_STRING;

} catch (StringEmptyWhiteSpaceException e) {
return null;
} catch (IndexOutOfBoundsException e) {
return bucketImageUrl;
}
}

String imagePath = bucketImageUrl.substring(bucketImageUrl.indexOf("/images"));
return imageOptimizerCdnUrl + imagePath + KB_IMAGE_OPTIMIZER_QUERY_STRING;
private void validateUrlEmptyOrWhiteSpace(String url) throws StringEmptyWhiteSpaceException {
if (url == null || url.trim().isEmpty()) {
throw new StringEmptyWhiteSpaceException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.oing.config.support;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE_PARAMETER, ElementType.PARAMETER})
public @interface RequestAppKey {
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ public PreSignedUrlResponse getProfileImagePreSignedUrl(String imageName) {
return new PreSignedUrlResponse(generatePreSignedUrl(generatePresignedUrlRequest));
}

@Override
public PreSignedUrlResponse getRealEmojiPreSignedUrl(String imageName) {
GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePreSignedUrlRequest("real-emoji",
imageName);

return new PreSignedUrlResponse(generatePreSignedUrl(generatePresignedUrlRequest));
}

private String generatePreSignedUrl(GeneratePresignedUrlRequest generatePresignedUrlRequest) {
String preSignedUrl;
try {
Expand Down
21 changes: 16 additions & 5 deletions gateway/src/main/java/com/oing/controller/CalendarController.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
import com.oing.service.MemberService;
import com.oing.util.OptimizedImageUrlGenerator;
import lombok.RequiredArgsConstructor;
import org.springframework.cglib.core.Local;
import org.springframework.stereotype.Controller;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.WeekFields;
import java.util.List;
import java.util.stream.IntStream;

Expand Down Expand Up @@ -64,20 +68,27 @@ private List<CalendarResponse> getCalendarResponses(List<String> familyIds, Loca
}

@Override
public ArrayResponse<CalendarResponse> getWeeklyCalendar(String yearMonth, Long week) {
List<String> familyIds = getFamilyIds();
LocalDate startDate = LocalDate.parse(yearMonth + "-01").plusWeeks(week - 1);
public ArrayResponse<CalendarResponse> getWeeklyCalendar(String yearMonth, Integer week) {
if (yearMonth == null) yearMonth = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
if (week == null) week = LocalDate.now().get(WeekFields.of(DayOfWeek.MONDAY, 1).weekOfMonth());

// 1주 = 해당 주차 (+ 0), 2주 이상 = 주차 추가 (+ (week - 1))
LocalDate startDate = LocalDate.parse(yearMonth + "-01").plusWeeks(week - 1); // yyyy-MM-dd 패턴으로 파싱
LocalDate endDate = startDate.plusWeeks(1);
List<String> familyIds = getFamilyIds();


List<CalendarResponse> calendarResponses = getCalendarResponses(familyIds, startDate, endDate);
return new ArrayResponse<>(calendarResponses);
}

@Override
public ArrayResponse<CalendarResponse> getMonthlyCalendar(String yearMonth) {
List<String> familyIds = getFamilyIds();
LocalDate startDate = LocalDate.parse(yearMonth + "-01");
if (yearMonth == null) yearMonth = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));

LocalDate startDate = LocalDate.parse(yearMonth + "-01"); // yyyy-MM-dd 패턴으로 파싱
LocalDate endDate = startDate.plusMonths(1);
List<String> familyIds = getFamilyIds();

List<CalendarResponse> calendarResponses = getCalendarResponses(familyIds, startDate, endDate);
return new ArrayResponse<>(calendarResponses);
Expand Down
Loading

0 comments on commit fda2632

Please sign in to comment.