v2_techspec_journeywishlist - 100-hours-a-week/KTB4-8th-BE GitHub Wiki
μ¬ν μμ리μ€νΈ λλ©μΈ
λ°μ΄ν°λ² μ΄μ€ μ€ν€λ§
- μ£Όμ ν
μ΄λΈ
member: νμ μ 보 (νμ ID)outing_wishlist_entry: μ¬ν μμ리μ€νΈ μμ (μλ² λ±)outing_course: μ¬ν μ½μ€ (μ¬ν μ½μ€ ID)outing_guide: μ₯μ/μ΄λ²€νΈ μ 보 (λ¨μΌ ν μ΄λΈ μμ)
API λͺ μΈ
λͺ©μ°¨
- μ¬ν μμ리μ€νΈμ μ¬ν μ½μ€ μΆκ° API
- μ¬ν μμ리μ€νΈμ μΆκ°ν μ₯μ λͺ©λ‘ μ‘°ν API
- μ¬ν μμ리μ€νΈμ μΆκ°ν νμ¬ λͺ©λ‘ μ‘°ν API
- μ¬ν μμ리μ€νΈμ μΆκ°ν μ¬ν μ½μ€ λͺ©λ‘ μ‘°ν API
- μ¬ν μμ리μ€νΈμ μΆκ°ν μ₯μ μμΈ μ‘°ν API
- μ¬ν μμ리μ€νΈμ μΆκ°ν νμ¬ μμΈ μ‘°ν API
- μ¬ν μμ리μ€νΈμ μΆκ°ν μ¬ν μ½μ€ μμΈ μ‘°ν API
- μ¬ν μμ리μ€νΈμ μΆκ°ν μ₯μ μ κ±° API
- μ¬ν μμ리μ€νΈμ μΆκ°ν νμ¬ μ κ±° API
- μ¬ν μμ리μ€νΈμ μΆκ°ν μ¬ν μ½μ€ μ κ±° API
μ¬ν μμ리μ€νΈμ μ¬ν μ½μ€ μΆκ° API
-
API λͺ μΈ:
POST /api/v2/member/outing-wishlist/outing-courses -
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ (μ ν¨νλ©° λΈλ리μ€νΈμ λ±μ¬λμ§ μμ μ‘μΈμ€ ν ν° λ³΄μ )
-
ꡬν μμΈ
-
μμ²
-
λ³Έλ¬Έ (
@Valid @RequestBody)outing_course_id:Long(@NotNull@Positive)
-
-
μ²λ¦¬ λ‘μ§
-
μμ² λ³Έλ¬Έ μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν
"INVALID_OUTING_COURSE_ID","μ ν¨νμ§ μμ μ½μ€ IDμ λλ€."
-
μ‘μΈμ€ ν ν°μμ νμ id μΆμΆ
Long.valueOf(jwt.getSubject)
-
μλΉμ€ κ³μΈ΅μ μμ²
member_id...Request
-
outing_course_idλ₯Ό ν΅ν΄ Repositoryμ ν΄λΉ μ¬ν μ½μ€μ μ‘΄μ¬ μ¬λΆ νμΈ-
μ¬ν μ½μ€κ° μ‘΄μ¬νλ μ§ νμΈ, μ‘΄μ¬νμ§ μμ μ 404 μ€λ₯ λ°ν
"OUTING_COURSE_NOT_FOUND","μ¬ν μ½μ€λ₯Ό μ°Ύμ μ μμ΅λλ€."-
.findById(outingCourseId).orElseThrow(() -> new OutingCourseNotFoundException()) - μ½μ€λ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
-
-
μ¬ν μμ리μ€νΈ μν°ν° μμ±
(member_id, type="place", outing_course_id, save_datetime)(memberId, outingCourseId, Instant.now())
-
Repositoryμ μ μ₯
-
λ§μ½ μ μ₯ μ€ ν΄λΉ μ½μ€κ° κΈ°μ‘΄μ μ μ₯λ μ½μ€μ μ€λ³΅μΈ κ²½μ° 409 μ€λ₯ λ°ν
"OUTING_COURSE_DUPLICATE","κΈ°μ‘΄μ μ μ₯λ μ½μ€μ μ€λ³΅λ©λλ€."-
...Repository.findByMemberIdAndOutingCourseId(memberId, outingCourseId) .ifPresent(() -> new OutingCourseDuplicateException()); ...Repository.save(...);
-
-
-
νΈλμμ κ΄λ¦¬
- μν°ν° μμ± λ° Repository μ μ₯μ μμμ μΌλ‘ μ²λ¦¬λμ΄μΌ ν¨
(member_id, outing_course_id)μ UNIQUE μ μ½ μ‘°κ±΄μ΄ νμν λ―?
-
μ¬ν μμ리μ€νΈμ μΆκ°ν μ₯μ λͺ©λ‘ μ‘°ν API
-
API λͺ μΈ:
GET /api/v2/member/outing-wishlist/places -
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ (μ ν¨νλ©° λΈλ리μ€νΈμ λ±μ¬λμ§ μμ μ‘μΈμ€ ν ν° λ³΄μ )
-
ꡬν μμΈ
-
νμ΄μ§μ
sizeλ 50μΌλ‘ μ€μ -
μ λ ¬ 쑰건μ μ΅κ·Ό μ μ₯ μ (
desc), ID(desc) -
μμ²
-
νλΌλ―Έν° (
@RequestParam)location:String(λ, κ΄μ/νΉλ³μμ μλ¬Έλͺ (ex:"incheon","daegu", ...),@NotBlank,required = false)cursor:String(μ‘°νν νμ΄μ§μ 컀μ,@NotBlank,required = false(μμ²μ μ 곡λμ§ μμ μ 첫 νμ΄μ§ μ‘°ν))
-
-
μ²λ¦¬ λ‘μ§
-
μμ² νλΌλ―Έν°κ° μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν
"INVALID_FILTER","μ ν¨νμ§ μμ νν° μ‘°κ±΄μ λλ€.""INVALID_CURSOR","μ ν¨νμ§ μμ 컀μμ λλ€."
-
μ‘μΈμ€ ν ν°μμ νμ id μΆμΆ
-
Long.valueOf(jwt.getSubject)
-
-
μλΉμ€ κ³μΈ΅μ μμ²
memberIdlocationcursor
-
cursorκ° κ°λ¦¬ν€λ νμ΄μ§μ μ‘΄μ¬ νμΈ, μμ μ 404 μ€λ₯ λ°ν"PAGE_NOT_FOUND","νμ΄μ§λ₯Ό μ°Ύμ μ μμ΅λλ€."
-
νμμ μμ리μ€νΈ μμ ν μ΄λΈκ³Ό μ₯μ ν μ΄λΈμ μ‘°μΈ,
locationμ λ°λΌ ν λ² λ νν°λ§ μ§νν κ²°κ³Ό λ°ν- μ₯μλ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
- μ₯μλ Soft Delete μ μ±
μ΄ μ μ©λμ΄
-
κ²μ κ²°κ³Όκ° 0κ±΄μΌ μ, λΉ λ¦¬μ€νΈ λ°ν
-
-
μ¬ν μμ리μ€νΈμ μΆκ°ν νμ¬ λͺ©λ‘ μ‘°ν API
-
API λͺ μΈ:
GET /api/v2/member/outing-wishlist/events -
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ (μ ν¨νλ©° λΈλ리μ€νΈμ λ±μ¬λμ§ μμ μ‘μΈμ€ ν ν° λ³΄μ )
-
ꡬν μμΈ
-
νμ΄μ§μ
sizeλ 50μΌλ‘ μ€μ -
μ λ ¬ 쑰건μ μ΅κ·Ό μ μ₯ μ (
desc), ID(desc) -
μμ²
-
νλΌλ―Έν° (
@RequestParam)ongoing:Boolean(νμ¬ μ΄μ μ€μΈ νμ¬λ§ μ‘°νν μ§ μ¬λΆ,@NotBlank,required = false)cursor:String(μ‘°νν νμ΄μ§μ 컀μ,@NotBlank,required = false(μμ²μ μ 곡λμ§ μμ μ 첫 νμ΄μ§ μ‘°ν))
-
-
μ²λ¦¬ λ‘μ§
-
μμ² νλΌλ―Έν°κ° μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν
"INVALID_FILTER","μ ν¨νμ§ μμ νν° μ‘°κ±΄μ λλ€.""INVALID_CURSOR","μ ν¨νμ§ μμ 컀μμ λλ€."
-
μ‘μΈμ€ ν ν°μμ νμ id μΆμΆ
-
Long.valueOf(jwt.getSubject)
-
-
μλΉμ€ κ³μΈ΅μ μμ²
memberIdongoingcursor
-
cursorκ° κ°λ¦¬ν€λ νμ΄μ§μ μ‘΄μ¬ νμΈ, μμ μ 404 μ€λ₯ λ°ν"PAGE_NOT_FOUND","νμ΄μ§λ₯Ό μ°Ύμ μ μμ΅λλ€."
-
νμμ μμ리μ€νΈ μμ ν μ΄λΈκ³Ό νμ¬ ν μ΄λΈμ μ‘°μΈνκ³ ,
ongoing = trueμνμ¬ μκ° < event.end_datetimeλ‘ νν°λ§ μ§νν κ²°κ³Ό λ°ν- νμ¬λ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
- νμ¬λ Soft Delete μ μ±
μ΄ μ μ©λμ΄
-
κ²μ κ²°κ³Όκ° 0κ±΄μΌ μ, λΉ λ¦¬μ€νΈ λ°ν
-
-
μ¬ν μμ리μ€νΈμ μΆκ°ν μ¬ν μ½μ€ λͺ©λ‘ μ‘°ν API
-
API λͺ μΈ:
GET /api/v2/member/outing-wishlist/outing-courses -
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ (μ ν¨νλ©° λΈλ리μ€νΈμ λ±μ¬λμ§ μμ μ‘μΈμ€ ν ν° λ³΄μ )
-
ꡬν μμΈ
-
νμ΄μ§μ
sizeλ 50μΌλ‘ μ€μ -
μ λ ¬ 쑰건μ μ΅κ·Ό μ μ₯ μ (
desc), ID(desc) -
μμ²
-
νλΌλ―Έν° (
@RequestParam)location:String(λ, κ΄μ/νΉλ³μμ μλ¬Έλͺ (ex: "incheon", "daegu", ...),@NotBlank,required = false)cursor:String(μ‘°νν νμ΄μ§μ 컀μ,@NotBlank,required = false(μμ²μ μ 곡λμ§ μμ μ 첫 νμ΄μ§ μ‘°ν))
-
-
μ²λ¦¬ λ‘μ§
-
μμ² νλΌλ―Έν°κ° μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν
"INVALID_FILTER","μ ν¨νμ§ μμ νν° μ‘°κ±΄μ λλ€.""INVALID_CURSOR","μ ν¨νμ§ μμ 컀μμ λλ€."
-
μ‘μΈμ€ ν ν°μμ νμ id μΆμΆ
-
Long.valueOf(jwt.getSubject)
-
-
μλΉμ€ κ³μΈ΅μ μμ²
memberIdlocationcursor
-
cursorκ° κ°λ¦¬ν€λ νμ΄μ§μ μ‘΄μ¬ νμΈ, μμ μ 404 μ€λ₯ λ°ν"PAGE_NOT_FOUND","νμ΄μ§λ₯Ό μ°Ύμ μ μμ΅λλ€."
-
νμμ μμ리μ€νΈ μμ ν μ΄λΈκ³Ό μ¬ν μ½μ€ ν μ΄λΈμ μ‘°μΈνκ³ ,
locationμ λ°λΌ ν λ² λ νν°λ§ μ§νν κ²°κ³Ό λ°ν- μ¬ν μ½μ€λ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
- μ¬ν μ½μ€λ Soft Delete μ μ±
μ΄ μ μ©λμ΄
-
κ²μ κ²°κ³Όκ° 0κ±΄μΌ μ, λΉ λ¦¬μ€νΈ λ°ν
-
-
μ¬ν μμ리μ€νΈμ μΆκ°ν μ₯μ μμΈ μ‘°ν API
-
API λͺ μΈ:
GET /api/v2/member/outing-wishlist/places/{placeId} -
μμ²
-
κ²½λ‘ λ³μ (
@PathVariable)placeId:Long(@Positive)
-
-
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ
-
ꡬν μμΈ
- μ²λ¦¬ λ‘μ§
-
placeIdμ μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν"INVALID_PLACE_ID","μ ν¨νμ§ μμ μ₯μ IDμ λλ€."
-
μλΉμ€ κ³μΈ΅μ μμ²
memberIdplaceId
-
placeIdμ μ‘΄μ¬ μ¬λΆ νμΈ, μ‘΄μ¬νμ§ μμ μ 404 μ€λ₯ λ°ν"PLACE_NOT_FOUND","μ₯μλ₯Ό μ°Ύμ μ μμ΅λλ€."-
...Repository.findById(placeId).orElseThrow(() -> new PlaceNotFoundException()) - μ₯μλ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
-
Repositoryμμ
placeIdκ° κ°λ¦¬ν€λ μ₯μ μ‘°ν ν λ°ν
-
- μ²λ¦¬ λ‘μ§
μ¬ν μμ리μ€νΈμ μΆκ°ν νμ¬ μμΈ μ‘°ν API
-
API λͺ μΈ:
GET /api/v2/member/outing-wishlist/events/{eventId} -
μμ²
-
κ²½λ‘ λ³μ (
@PathVariable)eventId:Long(@Positive)
-
-
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ
-
ꡬν μμΈ
- μ²λ¦¬ λ‘μ§
-
eventIdμ μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν"INVALID_EVENT_ID","μ ν¨νμ§ μμ νμ¬ IDμ λλ€."
-
μλΉμ€ κ³μΈ΅μ μμ²
memberIdeventId
-
eventIdμ μ‘΄μ¬ μ¬λΆ νμΈ, μ‘΄μ¬νμ§ μμ μ 404 μ€λ₯ λ°ν"EVENT_NOT_FOUND","νμ¬λ₯Ό μ°Ύμ μ μμ΅λλ€."-
...Repository.findById(eventId).orElseThrow(() -> new EventNotFoundException()) - νμ¬λ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
-
Repositoryμμ
eventIdκ° κ°λ¦¬ν€λ νμ¬ μ‘°ν ν λ°ν
-
- μ²λ¦¬ λ‘μ§
μ¬ν μμ리μ€νΈμ μΆκ°ν μ¬ν μ½μ€ μμΈ μ‘°ν API
-
API λͺ μΈ:
GET /api/v2/member/outing-wishlist/outing-courses/{outingCourseId} -
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ
-
μμ²
-
κ²½λ‘ λ³μ (
@PathVariable)outingCourseId:Long(@Positive)
-
-
ꡬν μμΈ
-
μ²λ¦¬ λ‘μ§
-
outingCourseIdμ μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν"INVALID_OUTING_COURSE_ID","μ ν¨νμ§ μμ μ¬ν μ½μ€ IDμ λλ€."
-
μλΉμ€ κ³μΈ΅μ μμ²
memberIdoutingCourseId
-
outingCourseIdμ μ‘΄μ¬ μ¬λΆ νμΈ, μ‘΄μ¬νμ§ μμ μ 404 μ€λ₯ λ°ν"OUTING_COURSE_NOT_FOUND","μ¬ν μ½μ€λ₯Ό μ°Ύμ μ μμ΅λλ€."-
...Repository.findById(outingCourseId).orElseThrow(() -> new OutingCourseNotFoundException) - μ¬ν μ½μ€λ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
-
Repositoryμμ
outingCourseIdκ° κ°λ¦¬ν€λ μ¬ν μ½μ€ μ‘°ν ν λ°ν
-
-
μ¬ν μμ리μ€νΈμμ μ₯μ μ κ±° API
-
API λͺ μΈ:
DELETE /api/v2/member/outing-wishlist/places/{placeId} -
μμ²
-
κ²½λ‘ λ³μ (
@PathVariable)placeId:Long(@Positive)
-
-
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ
-
ꡬν μμΈ
- μ²λ¦¬ λ‘μ§
-
placeIdμ μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν"INVALID_PLACE_ID","μ ν¨νμ§ μμ μ₯μ IDμ λλ€."
-
μλΉμ€ κ³μΈ΅μ μμ²
memberIdplaceId
-
placeIdμ μ‘΄μ¬ μ¬λΆ νμΈ, μ‘΄μ¬νμ§ μμ μ 404 μ€λ₯ λ°ν"PLACE_NOT_FOUND","μ₯μλ₯Ό μ°Ύμ μ μμ΅λλ€."-
...Repository.findById(placeId).orElseThrow(() -> new PlaceNotFoundException()) - μ₯μλ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
-
Repositoryμμ
placeIdκ° κ°λ¦¬ν€λ μ₯μ μμ .delete()
-
- μ²λ¦¬ λ‘μ§
-
νΈλμμ κ΄λ¦¬
- Soft Delete μ μ±
(
@Transactionalνμ)
- Soft Delete μ μ±
(
μ¬ν μμ리μ€νΈμμ νμ¬ μ κ±° API
-
API λͺ μΈ:
DELETE /api/v2/member/outing-wishlist/events/{eventId} -
μμ²
-
κ²½λ‘ λ³μ (
@PathVariable)eventId:Long(@Positive)
-
-
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ
-
ꡬν μμΈ
- μ²λ¦¬ λ‘μ§
-
eventIdμ μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν"INVALID_EVENT_ID","μ ν¨νμ§ μμ νμ¬ IDμ λλ€."
-
μλΉμ€ κ³μΈ΅μ μμ²
memberIdeventId
-
eventIdμ μ‘΄μ¬ μ¬λΆ νμΈ, μ‘΄μ¬νμ§ μμ μ 404 μ€λ₯ λ°ν"EVENT_NOT_FOUND","νμ¬λ₯Ό μ°Ύμ μ μμ΅λλ€."-
...Repository.findById(eventId).orElseThrow(() -> new EventNotFoundException()) - νμ¬λ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
-
Repositoryμμ
eventIdκ° κ°λ¦¬ν€λ νμ¬ μμ .delete()
-
- μ²λ¦¬ λ‘μ§
-
νΈλμμ κ΄λ¦¬
- Soft Delete μ μ±
(
@Transactionalνμ)
- Soft Delete μ μ±
(
μ¬ν μμ리μ€νΈμμ μ¬ν μ½μ€ μ κ±° API
-
API λͺ μΈ:
DELETE /api/v2/member/outing-wishlist/outing-courses/{outingCourseId} -
μμ²
-
κ²½λ‘ λ³μ (
@PathVariable)outingCourseId:Long(@Positive)
-
-
κΆν
- λ‘κ·ΈμΈν μ¬μ©μ
-
ꡬν μμΈ
-
μ²λ¦¬ λ‘μ§
-
outingCourseIdμ μ ν¨μ± κ²μ¦, μ€ν¨ μ 422 μ€λ₯ λ°ν"INVALID_OUTING_COURSE_ID","μ ν¨νμ§ μμ μ¬ν μ½μ€ IDμ λλ€."
-
μλΉμ€ κ³μΈ΅μ μμ²
memberIdoutingCourseId
-
outingCourseIdμ μ‘΄μ¬ μ¬λΆ νμΈ, μ‘΄μ¬νμ§ μμ μ 404 μ€λ₯ λ°ν"OUTING_COURSE_NOT_FOUND","μ¬ν μ½μ€λ₯Ό μ°Ύμ μ μμ΅λλ€."-
...Repository.findById(outingCourseId).orElseThrow(() -> new OutingCourseNotFoundException()) - μ¬ν μ½μ€λ Soft Delete μ μ±
μ΄ μ μ©λμ΄
WHERE delete_datetime = nullμ μΆκ° 쑰건 νμ
-
Repositoryμμ
outingCourseIdκ° κ°λ¦¬ν€λ μ¬ν μ½μ€ μμ .delete()
-
-
-
νΈλμμ κ΄λ¦¬
- Soft Delete μ μ±
(
@Transactionalνμ)
- Soft Delete μ μ±
(