Skip to content

🧳 νŠΈλ¦¬ν”Œμ—¬ν–‰μž 클럽 λ§ˆμΌλ¦¬μ§€ μ„œλΉ„μŠ€

Notifications You must be signed in to change notification settings

litsynp/triple-club-mileage-service

Repository files navigation

🧳 νŠΈλ¦¬ν”Œμ—¬ν–‰μž 클럽 λ§ˆμΌλ¦¬μ§€ μ„œλΉ„μŠ€

νŠΈλ¦¬ν”Œ μ‚¬μš©μžλ“€μ΄ μž₯μ†Œμ— 리뷰λ₯Ό μž‘μ„±ν•  λ•Œ 포인트λ₯Ό λΆ€μ—¬ν•˜κ³ , 전체/κ°œμΈμ— λŒ€ν•œ 포인트 λΆ€μ—¬ νžˆμŠ€ν† λ¦¬μ™€, κ°œμΈλ³„ λˆ„μ  포인트λ₯Ό κ΄€λ¦¬ν•˜κ³ μž ν•©λ‹ˆλ‹€.

Spring Boot μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ΄μš©ν•˜μ—¬ 주어진 문제λ₯Ό ν•΄κ²°ν•˜κ³  κ·Έ λ°©μ•ˆμ— λŒ€ν•΄μ„œ μ΄μ•ΌκΈ°ν•©λ‹ˆλ‹€.

πŸ” κ΅¬ν˜„ κΈ°λŠ₯

  • μž₯μ†Œμ— 리뷰λ₯Ό μž‘μ„±ν•˜λ©΄ 포인트λ₯Ό λΆ€μ—¬
  • 전체/κ°œμΈμ— λŒ€ν•œ 포인트 λΆ€μ—¬ νžˆμŠ€ν† λ¦¬ 관리
  • κ°œμΈλ³„ λˆ„μ  포인트 관리

βœ… μš”κ΅¬μ‚¬ν•­ 및 체크리슀트

  • MySQL β‰₯ 5.7 μ‚¬μš©
  • ν…Œμ΄λΈ”κ³Ό μΈλ±μŠ€μ— λŒ€ν•œ DDL μž‘μ„±
  • μ• ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ λ‹€μŒ API 제곡
    • POST /events 둜 ν˜ΈμΆœν•˜λŠ” 포인트 적립 API
    • 포인트 쑰회 API
  • 상세 μš”κ΅¬μ‚¬ν•­
    • REST APIλ₯Ό μ œκ³΅ν•˜λŠ” μ„œλ²„ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κ΅¬ν˜„
    • Java, Kotlin, Python, JavaScript(or TypeScript) 쀑 μ–Έμ–΄ 선택
    • Framework, Library 자유 μ‚¬μš©, μΆ”κ°€ Data Storage ν•„μš”μ‹œ μ—¬λŸ¬ μ’…λ₯˜ μ‚¬μš© κ°€λŠ₯
    • README μž‘μ„±
    • ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ μž‘μ„± (Optional)

❓ μ‚¬μš© 방법

λ¨Όμ € docker compose둜 MySQL μ»¨ν…Œμ΄λ„ˆλ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€. (docker-compose.yml λ‚΄μš©)

$ docker compose up

μ»¨ν…Œμ΄λ„ˆκ°€ 쀀비됐닀면 μŠ€ν”„λ§ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λΉŒλ“œ ν›„ μ‹€ν–‰ν•©λ‹ˆλ‹€.

$ ./gradlew build && java -jar build/libs/mileage-service-0.0.1-SNAPSHOT.jar

μ„œλ²„κ°€ λ„μ›Œμ‘Œλ‹€λ©΄

  • 리뷰 μž‘μ„± 이벀트 API인 POST http://localhost:8080/events

    {
        "type": "REVIEW",
        "action": "ADD",
        "reviewId": "ff35e929-fcf6-11ec-b3c2-0242ac170002",
        "userId": "31313130-3031-3131-3130-000000000000",
        "placeId": "8040a09f-fcf6-11ec-b3c2-0242ac170002",
        "content": "μ’‹μ•„μš”!",
        "attachedPhotoIds": ["48925641-70f3-4674-86e6-420bbab59bf8", "cf00ec57-563b-4f0e-b5bf-78ce28738efb"]
    }
  • μ‚¬μš©μž 포인트 총점 쑰회 API인 GET http://localhost:8080/points?user-id=31313130-3031-3131-3130-000000000000

  • νŽ˜μ΄μ§€λ„€μ΄μ…˜μ„ μ œκ³΅ν•˜λŠ” μ‚¬μš©μž 포인트 기둝 쑰회 API인 GET http://localhost:8080/point-histories

와 같이, API λͺ…세에 맞게 싀행해보싀 수 μžˆμŠ΅λ‹ˆλ‹€.

  • 미리 μ‚¬μš©μž 2λͺ…, μž₯μ†Œ 1개, 사진 2개λ₯Ό src/main/resources/data.sql에 λ„£μ–΄μ„œ 진행할 수 μžˆλ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. ν•„μš”μ— 따라 SQL을 μ‹€ν–‰ν•˜μ‹œλ©΄ λ©λ‹ˆλ‹€.

βš™οΈ μ£Όμš” μ‚¬μš© ν”„λ ˆμž„μ›Œν¬ / 라이브러리 및 버전

  • Spring Boot 2.7.1
    • Querydsl JPA 5.0.0
    • Spring REST Docs (API λ¬Έμ„œν™”)
    • p6spy (SQL logging)
  • Java 11
  • Gradle 7.4.1
  • MySQL (8.0.29) (InnoDB)
  • Docker & docker compose (MySQL μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰)

πŸš— ν”„λ‘œμ νŠΈ ꡬ쑰 및 μ•„ν‚€ν…μ²˜ μš”μ•½

3-tier-layered-architecture

좜처: https://www.petrikainulainen.net/software-development/design/understanding-spring-web-application-architecture-the-classic-way/

  • ν”„λ‘œμ νŠΈ κ΅¬μ‘°λŠ” μœ„μ™€ 같이 3 tier layered architecture둜 κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€. Web, Service, Repository둜 κ΅¬λΆ„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

  • ν΄λΌμ΄μ–ΈνŠΈ ↔ Controller에 μ‚¬μš©λ˜λŠ” DTO와, Controller ↔ Service에 μ‚¬μš©λ˜λŠ” DTOλ₯Ό κ΅¬λΆ„ν•˜μ—¬ κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

  • API controller ν΄λž˜μŠ€λŠ” api, service ν΄λž˜μŠ€λŠ” service, repositoryλŠ” dao, entityλŠ” domain νŒ¨ν‚€μ§€μ— 관심사에 따라 λͺ¨μ•„λ‘μ—ˆμŠ΅λ‹ˆλ‹€.

  • 각 κ³„μΈ΅μ—μ„œ μ‚¬μš©λ˜λŠ” DTOλŠ” dto νŒ¨ν‚€μ§€μ— μš©λ„μ— 따라 λͺ¨μ•„λ‘μ—ˆμŠ΅λ‹ˆλ‹€.

    • 각 DTOμ—λŠ” @NotNullκ³Ό 같은 μ–΄λ…Έν…Œμ΄μ…˜μ„ μ΄μš©ν•œ validation이 μ μš©λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
  • ν†΅μΌλœ μ–‘μ‹μ˜ exception handling을 μœ„ν•΄ global.error νŒ¨ν‚€μ§€μ— exception handler 및 exception, error code 등을 λͺ¨μ•„λ‘μ—ˆμŠ΅λ‹ˆλ‹€.

πŸ‘₯ SQL Schema 및 ERD

λ‹€μŒμ€ μš”κ΅¬μ‚¬ν•­μ„ λ°”νƒ•μœΌλ‘œ μž‘μ„±ν•œ ERDμž…λ‹ˆλ‹€.

triple-erd

μŠ€ν‚€λ§ˆλŠ” src/main/resources/schema.sql 에 μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

DDL Schema μž‘μ„± 및 unique & foreign constraint, index 섀정을 ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

DB Engine은 InnoDB둜 μ‚¬μš©ν•©λ‹ˆλ‹€.

🧾 API λͺ…μ„Έ

EndpointλŠ” 리뷰 μž‘μ„±, μˆ˜μ •, μ‚­μ œ 이벀트λ₯Ό μ „λ‹¬ν•˜λŠ” /events, μ‚¬μš©μž 점수 합계λ₯Ό ν™•μΈν•˜λŠ” /points, μ‚¬μš©μž 점수 기둝 λͺ©λ‘μ„ μ‘°νšŒν•˜λŠ” /point-histories 둜 총 3κ°œμž…λ‹ˆλ‹€. 이벀트의 μ’…λ₯˜κΉŒμ§€ κ³„μ‚°ν•˜λ©΄ 5개의 APIκ°€ λ©λ‹ˆλ‹€.

API λͺ…μ„ΈλŠ” Spring REST Docs을 μ΄μš©ν•΄ ν…ŒμŠ€νŠΈλ₯Ό 톡해 λ¬Έμ„œν™”ν–ˆμŠ΅λ‹ˆλ‹€.

http://localhost:8080/docs/index.html 둜 μ ‘μ†ν•˜λ©΄ ν™•μΈν•˜μ‹€ 수 μžˆμŠ΅λ‹ˆλ‹€.

μΆ”κ°€λ‘œ PDF둜 μ œμž‘ν•˜μ—¬ μ²¨λΆ€ν•©λ‹ˆλ‹€. 트라플 클ᄅα…₯α†Έ α„†α…‘α„‹α…΅α†―α„…α…΅α„Œα…΅ ᄉα…₯바스 API PDF

πŸ‘ Test κ²°κ³Ό

JUnit 5, Assertj, BDDMockito, Spring REST Docs 및 MockMvc 등을 톡해 μœ λ‹› ν…ŒμŠ€νŠΈ 및 톡합 ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

image

🏁 REMARKS ν•΄κ²° λ°©μ•ˆ

μš”κ΅¬μ‚¬ν•­ λ¬Έμ„œμ˜ Remarks에 적힌 각 λ¬Έμ œμ— λŒ€ν•œ ν•΄κ²° λ°©μ•ˆμž…λ‹ˆλ‹€.

βœ…οΈ ν•œ μ‚¬μš©μžλŠ” μž₯μ†Œλ§ˆλ‹€ 리뷰λ₯Ό 1개만 μž‘μ„±ν•  수 있고, λ¦¬λ·°λŠ” μˆ˜μ • λ˜λŠ” μ‚­μ œν•  수 μžˆλ‹€.

ν•œ μ‚¬μš©μžκ°€ μž₯μ†Œλ§ˆλ‹€ 리뷰λ₯Ό 1개만 μž‘μ„±ν•  수 μžˆλŠ” 쑰건은 미리 DB에 unique constraintλ₯Ό κ±Έμ—ˆμŠ΅λ‹ˆλ‹€.

alter table review
    add unique review_ak01 (user_id, place_id);

이후 리뷰 μž‘μ„± 이벀트 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μ²˜λ¦¬ν•˜λŠ” μ„œλΉ„μŠ€ λ©”μ„œλ“œμΈ ReviewService.writeReview() μ—μ„œλ„ ν•΄λ‹Ή μž₯μ†Œμ— λŒ€ν•΄ μ‚¬μš©μžκ°€ μž‘μ„±ν•œ 리뷰가 이미 μ‘΄μž¬ν•˜λŠ”μ§€λ₯Ό ν™•μΈν•˜κ³ , μ‘΄μž¬ν•œλ‹€λ©΄ 409 CONFLICT λ₯Ό λ°˜ν™˜ν•˜λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€.

리뷰 μˆ˜μ • λ˜λŠ” μ‚­μ œλŠ” 이벀트 APIμ—μ„œ ADD 외에도 MOD 및 DELETE action을 μ§€μ›ν•˜μ—¬ ν•΄κ²°ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

βœ…οΈ 리뷰 보상 μ μˆ˜κ°€ μ‘΄μž¬ν•œλ‹€.

리뷰 보상 μ μˆ˜λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

λ‚΄μš© 점수
- 1자 이상 ν…μŠ€νŠΈ μž‘μ„±: 1점
- 1μž₯ 이상 사진 첨뢀: 1점

λ³΄λ„ˆμŠ€ 점수
- νŠΉμ • μž₯μ†Œμ— 첫 리뷰 μž‘μ„±: 1점

μ„œλΉ„μŠ€ λ©”μ„œλ“œμΈ ReviewService.writeReview() μ—μ„œ 점수λ₯Ό κ³„μ‚°ν•˜λŠ” κ²ƒμœΌλ‘œ ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€. λ’€μ—μ„œ μ„€λͺ…ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

βœ… 포인트 증감이 μžˆμ„ λ•Œλ§ˆλ‹€ 이λ ₯이 남아야 ν•œλ‹€.

μž‘μ„±, μˆ˜μ •, μ‚­μ œ Review 이벀트λ₯Ό POST /events APIλ₯Ό 톡해 전달할 λ•Œλ§ˆλ‹€ μ‚¬μš©μžμ˜ ν¬μΈνŠΈκ°€ λ³€λ™λ˜λ©°, κ·Έ 기둝은 μ‹œκ°„κ³Ό ν•¨κ»˜ μ–Όλ§ˆλ‚˜ λ³€λ™λλŠ”μ§€ 이λ ₯이 λ‚¨μŠ΅λ‹ˆλ‹€.

μš”μ²­μœΌλ‘œ μ „λ‹¬λœ DTOλ₯Ό 읽어 μ–΄λ–€ μœ ν˜•μ˜ μ΄λ²€νŠΈμΈμ§€ νŒλ‹¨ν•˜μ—¬, ReviewService ν΄λž˜μŠ€μ— μ •μ˜λœ writeReview, updateReview, deleteReviewById λ©”μ„œλ“œκ°€ ν˜ΈμΆœλ©λ‹ˆλ‹€.

βœ… μ‚¬μš©μžλ§ˆλ‹€ ν˜„μž¬ μ‹œμ μ˜ 포인트 총점을 μ‘°νšŒν•˜κ±°λ‚˜ 계산할 수 μžˆμ–΄μ•Ό ν•œλ‹€.

각 μ‚¬μš©μžμ˜ 포인트의 총점은 GET /points API둜 μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€. Request parameter둜 UUID인 μ‚¬μš©μž IDλ₯Ό 전달해야 ν•˜λ©° (예: GET /points?user-id=…), 포인트의 총점을 λ‹€μŒκ³Ό 같은 ν˜•μ‹μœΌλ‘œ λ°˜ν™˜ν•©λ‹ˆλ‹€.

{
  "userId": "<μ‚¬μš©μž ID>",
  "points": 4
}

βœ… 포인트 λΆ€μ—¬ API κ΅¬ν˜„μ— ν•„μš”ν•œ SQL μˆ˜ν–‰ μ‹œ, 전체 ν…Œμ΄λΈ” μŠ€μΊ”μ΄ μΌμ–΄λ‚˜μ§€ μ•ŠλŠ” μΈλ±μŠ€κ°€ ν•„μš”ν•˜λ‹€.

MySQLμ—μ„œ InnoDBλ₯Ό μ‚¬μš©ν•˜μ˜€μœΌλ©°, μ™Έλž˜ν‚€ 섀정을 ν•˜λ©΄ 인덱슀 섀정이 λ©λ‹ˆλ‹€. κ·Έλ ‡μ§€λ§Œ μΆ”κ°€λ‘œ μ™Έλž˜ν‚€μ—λ„ 인덱슀 섀정을 ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ 섀정은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μ‚¬μš©μž 점수 ν…Œμ΄λΈ”μΈ user_point 에 μ‚¬μš©μž 점수 변동 기둝 100개λ₯Ό λ„£μ—ˆμŠ΅λ‹ˆλ‹€.

  • 100개 μ€‘μ—μ„œ 1개의 기둝만이 μ‚¬μš©μžκ°€ λ³΄μœ ν•œ 점수 κΈ°λ‘μž…λ‹ˆλ‹€.

  • schema.sql νŒŒμΌμ— μ •μ˜λœ ν…Œμ΄λΈ” DDLμ—μ„œ ν…Œμ΄λΈ”μ„ 생성할 λ•Œ engine = InnoDB 둜 InnoDB둜 μ„€μ •ν•΄λ‘μ—ˆμŠ΅λ‹ˆλ‹€.

  • μœ„μ—μ„œ λ§μ”€λ“œλ Έλ‹€μ‹œν”Ό, MySQL InnoDBλŠ” μ™Έλž˜ν‚€μ— λŒ€ν•΄μ„œλŠ” indexκ°€ μžλ™μœΌλ‘œ μ μš©λ©λ‹ˆλ‹€. κ·Έλ ‡μ§€λ§Œ foreign key constraint 말고도 index도 μΆ”κ°€λ‘œ κ±Έμ—ˆμŠ΅λ‹ˆλ‹€.

user_id에 λŒ€ν•΄μ„œ λ‹€μŒκ³Ό 같이 indexλ₯Ό μ μš©ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

alter table user_point
    add index user_point_ak01 (user_id);

그리고 λ‹€μŒ 쿼리 ν”Œλžœμ„ ν™•μΈν•˜κΈ° μœ„ν•΄ queryλ₯Ό μ‹€ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

explain
select coalesce(sum(amount), 0)
from user_point
where user_point.user_id = @user_id;

κ²°κ³ΌλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

select-sum-user-point-query-plan

type은 ref, Using index condition으둜, 인덱슀λ₯Ό μ‚¬μš©ν•΄ μ‘°νšŒν•˜κ³  있으며, 전체 ν…Œμ΄λΈ” μŠ€μΊ”μ΄ μΌμ–΄λ‚˜μ§€ μ•Šμ€ 것을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

βœ… 리뷰λ₯Ό μž‘μ„±ν–ˆλ‹€κ°€ μ‚­μ œν•˜λ©΄ ν•΄λ‹Ή 리뷰둜 λΆ€μ—¬ν•œ λ‚΄μš© μ μˆ˜μ™€ λ³΄λ„ˆμŠ€ μ μˆ˜λŠ” νšŒμˆ˜ν•œλ‹€.

리뷰λ₯Ό μž‘μ„±ν•˜λ©΄ user_point ν…Œμ΄λΈ”μ— λ‹€μŒκ³Ό 같은 pseudo code처럼 μ μˆ˜κ°€ μΆ”κ°€λ©λ‹ˆλ‹€.

amount = 0

# 1자 이상 ν…μŠ€νŠΈ μž‘μ„±: 1점
if len(dto.content) > 0:
    amount = amount + 1

# 1μž₯ 이상 사진 첨뢀: 1점
if len(dto.attachedPhotoIds):
    amount = amount + 1

# 첫 리뷰 μž‘μ„±: 1점
if not exists(review where review.placeId = dto.placeId):
    amount = amount + 1

if amount > 0:
    newUserPoint = UserPoint(user, review, amount)
    save newUserPoint to user_point table

즉, μž‘μ„±μ—μ„œ λ°œμƒν•œ μ μˆ˜κ°€ 1 이상일 λ•Œλ§Œ μ €μž₯ν•©λ‹ˆλ‹€.

user_point ν…Œμ΄λΈ”μ€ review ν…Œμ΄λΈ”κ³Ό FK둜 μ—°κ²°λœ ν…Œμ΄λΈ”μž…λ‹ˆλ‹€. ON DELETE SET NULL μ˜΅μ…˜μœΌλ‘œ μ‚­μ œλ  λ•Œ 리뷰가 μ‚­μ œλ˜λ”λΌλ„ μ‚¬μš©μž 점수 기둝은 μ‚­μ œλ˜μ§€ μ•Šκ³  FKκ°€ null이 λ˜λ„λ‘ ν•΄μ„œ 기둝을 μœ μ§€ν•©λ‹ˆλ‹€.

alter table user_point
    add constraint user_point_fk02 foreign key (review_id) references review (id) on delete set null on update cascade;

점수λ₯Ό νšŒμˆ˜ν•  λ•Œμ—λŠ” λ‹€μŒκ³Ό 같이 μ§„ν–‰λ©λ‹ˆλ‹€.

# 리뷰λ₯Ό μ‚­μ œν•˜λ©΄ ν•΄λ‹Ή 리뷰둜 λΆ€μ—¬ν•œ λ‚΄μš© μ μˆ˜μ™€ λ³΄λ„ˆμŠ€ 점수 회수
# ν•˜μ§€λ§Œ 기둝 μœ μ§€λ₯Ό μœ„ν•΄ μ‚­μ œν•΄μ„œλŠ” μ•ˆλœλ‹€.
pointsFromReview = getUserPoints(user=review.user, review=review) # e.g.) 3

# ν•΄λ‹Ή λ¦¬λ·°λ‘œλΆ€ν„° 얻은 점수λ₯Ό κ³„μ‚°ν•˜μ—¬ νšŒμˆ˜ν•œλ‹€.
if pointsFromReview > 0L:
    # λ¦¬λ·°λ‘œλΆ€ν„° 받은 κ°’λ§ŒνΌ μ°¨κ°ν•˜λ©΄ λœλ‹€
    newUserPoint = UserPoint(user, review, amount=-pointsFromReview) # e.g) -3
    save newUserPoint to user_point table

총점을 κ³„μ‚°ν•΄μ„œ μ‚­μ œν•˜λ―€λ‘œ, 리뷰 μˆ˜μ • λ“±μœΌλ‘œ 회수된 μ μˆ˜κΉŒμ§€ κ³ λ €ν•΄μ„œ μ΅œμ’…μ μœΌλ‘œ νšŒμˆ˜ν•  μ μˆ˜κ°€ κ³„μ‚°λ©λ‹ˆλ‹€.

λ¦¬λ·°λŠ” 이후 μ‚­μ œν•˜κ²Œ λ©λ‹ˆλ‹€.

μ‚­μ œ ν›„, 같은 μž₯μ†Œμ— μ‚¬μš©μžκ°€ 리뷰λ₯Ό μž‘μ„±ν•˜κ²Œ λœλ‹€λ©΄, review ν…Œμ΄λΈ”μ— λ¦¬λ·°λŠ” μ—†κΈ° λ•Œλ¬Έμ— λ³΄λ„ˆμŠ€ 점수 계산이 처음 μž‘μ„±ν•˜λŠ” 것과 λ™μΌν•˜κ²Œ μ§„ν–‰λ©λ‹ˆλ‹€.

βœ… 리뷰λ₯Ό μˆ˜μ •ν•˜λ©΄ μˆ˜μ •ν•œ λ‚΄μš©μ— λ§žλŠ” λ‚΄μš© 점수λ₯Ό κ³„μ‚°ν•˜μ—¬ 점수λ₯Ό λΆ€μ—¬ν•˜κ±°λ‚˜ νšŒμˆ˜ν•œλ‹€.

μš”κ΅¬μ‚¬ν•­μ— λ”°λ₯΄λ©΄ 리뷰λ₯Ό μˆ˜μ •ν•˜λ©΄ λ‹€μŒκ³Ό 같이 μ§„ν–‰λ©λ‹ˆλ‹€.

1. κΈ€λ§Œ μž‘μ„±ν•œ 리뷰에 사진을 μΆ”κ°€ν•˜λ©΄ 1점을 λΆ€μ—¬
2. κΈ€κ³Ό 사진이 μžˆλŠ” λ¦¬λ·°μ—μ„œ 사진을 λͺ¨λ‘ μ‚­μ œν•˜λ©΄ 1점을 회수

μ‚¬μ§„λ§Œ 있고 글이 μ—†λŠ” 리뷰가 μ‘΄μž¬ν•  수 μžˆλ‹€κ³  κ°€μ •ν•˜κ³  κ°œλ°œν–ˆμŠ΅λ‹ˆλ‹€.

이 가정에 λ”°λ₯Έλ‹€λ©΄, 1, 2번 쑰건은 λͺ¨λ‘ 글이 μžˆμ–΄μ•Ό ν•œλ‹€λŠ” 것을 μ „μ œλ‘œ ν•˜μ§€λ§Œ, κ·Έλ ‡κ²Œ ν•œλ‹€λ©΄ 2번 쑰건을 μ•…μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  1. 글을 λ¨Όμ € μ‚­μ œν•˜κ³ , 이후 사진을 μ‚­μ œν•˜λ©΄ 2번 쑰건의 κΈ€κ³Ό 사진이 μžˆλŠ” λ¦¬λ·°μ—μ„œ λΌλŠ” μ „μ œμ‘°κ±΄μ„ νšŒν”Όν•΄ 회수λ₯Ό νšŒν”Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  2. 이후 λ‹€μ‹œ 글을 μž‘μ„±ν•œ λ’€, 사진을 μΆ”κ°€ν•˜λ©΄ 1번 쑰건의 κΈ€λ§Œ μž‘μ„±ν•œ 리뷰에 사진을 μΆ”κ°€ 쑰건이 λ°˜μ˜λ˜μ–΄ 또 1점을 μ–»κ²Œ λ©λ‹ˆλ‹€.

λ”°λΌμ„œ 글이 μžˆλ“  μ—†λ“  사진이 λ³€λ™λœλ‹€λ©΄ μ μˆ˜μ— 변동을 주도둝 ν–ˆμŠ΅λ‹ˆλ‹€.

이 점수 계산은 λ‹€μŒκ³Ό 같이 μ§„ν–‰ν–ˆμŠ΅λ‹ˆλ‹€.

1. 리뷰에 사진이 이전에 μ—†μ—ˆλŠ”μ§€ ν™•μΈν•œλ‹€.
2. 사진이 μ—†μ—ˆλŠ”λ° 사진을 1μž₯ 이상 μΆ”κ°€ν–ˆλ‹€λ©΄ 1점을 λΆ€μ—¬ν•œλ‹€.
3. 사진이 1μž₯ 이상 μžˆμ—ˆλŠ”λ° 사진을 λͺ¨λ‘ μ‚­μ œν–ˆλ‹€λ©΄, ν•΄λ‹Ή 리뷰λ₯Ό 톡해 1점을 λΆ€μ—¬ν•œ 적이 μžˆλ‹€λ©΄ 1점을 μ°¨κ°ν•œλ‹€.

리뷰 μˆ˜μ •μ˜ 점수 계산은 λ‹€μŒκ³Ό 같은 pseudo code둜 κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

# 이전에 사진이 μžˆμ—ˆλŠ”μ§€ 확인
emptyPhotosBefore = len(review.attachedPhotos) == 0

# κΈ°μ‘΄ 리뷰에 μ €μž₯된 사진 쀑, μƒˆλ‘œ μΆ”κ°€λœ 사진이 μ•„λ‹Œ 사진은 μ „λΆ€ μ‚­μ œ
review.photos.filter(photo.id not in dto.attachedPhotoIds).delete()

# μƒˆλ‘œ μΆ”κ°€λœ 사진 μ €μž₯
review.photos.addAll(dto.attachedPhotoIds)

## 리뷰λ₯Ό μˆ˜μ •ν•˜λ©΄ μˆ˜μ •ν•œ λ‚΄μš©μ— λ§žλŠ” λ‚΄μš© 점수λ₯Ό κ³„μ‚°ν•˜μ—¬ 점수λ₯Ό λΆ€μ—¬ν•˜κ±°λ‚˜ 회수 ##

# κΈ€λ§Œ μž‘μ„±ν•œ 리뷰에 사진을 μΆ”κ°€ν•˜λ©΄ 1점을 μΆ”κ°€
if emptyPhotosBefore and len(review.photos) != 0:
    newUserPoint = UserPoint(user, review, amount=1)
    save newUserPoint to userPoint table

# κΈ€κ³Ό 사진이 μžˆλŠ” λ¦¬λ·°μ—μ„œ 사진을 λͺ¨λ‘ μ‚­μ œν•˜λ©΄ 1점을 회수
if (not emptyPhotosBefore) and len(review.photos) == 0:
    userPoints = getAllUserPoints(user.id)
    if (userPoints > 0):
        newUserPoint = UserPoint(user, review, amount=-1)
    save newUserPoint to userPoint table

μœ„μ™€ 같이 글을 올리기 μ „μ˜ μ‚¬μ§„μ˜ 갯수, 글을 올린 ν›„μ˜ μ‚¬μ§„μ˜ 갯수λ₯Ό μ΄μš©ν•˜μ—¬ 점수λ₯Ό κ³„μ‚°ν–ˆμŠ΅λ‹ˆλ‹€.

* μš”κ΅¬μ‚¬ν•­μ— λ”°λ₯΄λ©΄ 글을 μž‘μ„±ν•œλ‹€λ©΄ 1점이 μΆ”κ°€λ˜μ§€λ§Œ, 글이 μ—†λŠ” μƒνƒœμ—μ„œ 글을 μΆ”κ°€ν•˜κ±°λ‚˜, 글이 μžˆλŠ” μƒνƒœμ—μ„œ 없도둝 μˆ˜μ •ν•˜λ”λΌλ„ 포인트의 λ³€ν™”λŠ” μΌμ–΄λ‚˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

βœ… μ‚¬μš©μž μž…μž₯μ—μ„œ λ³Έ β€˜μ²« 리뷰'일 λ•Œ λ³΄λ„ˆμŠ€ 점수λ₯Ό λΆ€μ—¬ν•œλ‹€.

1. μ–΄λ–€ μž₯μ†Œμ— μ‚¬μš©μž Aκ°€ 리뷰λ₯Ό 남겼닀가 μ‚­μ œν•˜κ³ , μ‚­μ œλœ 이후 μ‚¬μš©μž Bκ°€ 리뷰λ₯Ό 남기면 μ‚¬μš©μž Bμ—κ²Œ λ³΄λ„ˆμŠ€ 점수λ₯Ό λΆ€μ—¬ν•œλ‹€.
2. μ–΄λ–€ μž₯μ†Œμ— μ‚¬μš©μž Aκ°€ 리뷰λ₯Ό 남겼닀가 μ‚­μ œν•˜λŠ”λ°, μ‚­μ œλ˜κΈ° 이전 μ‚¬μš©μž Bκ°€ 리뷰λ₯Ό 남기면 μ‚¬μš©μž Bμ—κ²Œ 점수λ₯Ό λΆ€μ—¬ν•˜μ§€ μ•ŠλŠ”λ‹€.

1, 2λ₯Ό λ™μ‹œμ— κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ‹¨μˆœνžˆ 리뷰λ₯Ό μ‚­μ œν•˜λ©΄ 리뷰 ν…Œμ΄λΈ”μ—μ„œ μ‚­μ œν•˜λ©΄ λ©λ‹ˆλ‹€. 그리고 리뷰λ₯Ό μž‘μ„±ν•˜λŠ” μ‹œμ μ— ν•΄λ‹Ή μž₯μ†Œμ— 리뷰λ₯Ό 남긴 μ‚¬λžŒμ΄ μ—†λŠ”μ§€ ν™•μΈν•˜κ³  점수λ₯Ό κ³„μ‚°ν•˜λ©΄ λ©λ‹ˆλ‹€.

λ”°λΌμ„œ λ‹€μŒκ³Ό 같이 μ§„ν–‰λ©λ‹ˆλ‹€.

1. μ–΄λ–€ μž₯μ†Œμ— μ‚¬μš©μž Aκ°€ 리뷰λ₯Ό 남겼닀가 μ‚­μ œν•˜κ³ , μ‚­μ œλœ 이후 μ‚¬μš©μž Bκ°€ 리뷰λ₯Ό 남기면 μ‚¬μš©μž Bμ—κ²Œ λ³΄λ„ˆμŠ€ 점수λ₯Ό λΆ€μ—¬ν•œλ‹€.
    1. μ‚¬μš©μž Aκ°€ μž₯μ†Œ P에 리뷰λ₯Ό 남긴닀.
    2. μ‚¬μš©μž Aκ°€ 리뷰λ₯Ό μ‚­μ œν•œλ‹€. μ‚¬μš©μž A의 회수 ν¬μΈνŠΈκ°€ κ³„μ‚°λ˜μ–΄ κΈ°λ‘λœλ‹€.
    3. μ‚¬μš©μž Bκ°€ μž₯μ†Œ P에 리뷰λ₯Ό 남긴닀. ν•΄λ‹Ή μž₯μ†Œμ— 리뷰가 μ—†μœΌλ―€λ‘œ λ³΄λ„ˆμŠ€ 1점 μΆ”κ°€ν•œλ‹€.

2. μ–΄λ–€ μž₯μ†Œμ— μ‚¬μš©μž Aκ°€ 리뷰λ₯Ό 남겼닀가 μ‚­μ œν•˜λŠ”λ°, μ‚­μ œλ˜κΈ° 이전 μ‚¬μš©μž Bκ°€ 리뷰λ₯Ό 남기면 μ‚¬μš©μž Bμ—κ²Œ 점수λ₯Ό λΆ€μ—¬ν•˜μ§€ μ•ŠλŠ”λ‹€.
    1. μ‚¬μš©μž Aκ°€ μž₯μ†Œ P에 리뷰λ₯Ό 남긴닀.
    2. μ‚¬μš©μž Bκ°€ μž₯μ†Œ P에 리뷰λ₯Ό 남긴닀. 이미 ν•΄λ‹Ή μž₯μ†Œμ— 리뷰가 μžˆμœΌλ―€λ‘œ λ³΄λ„ˆμŠ€ μ μˆ˜λŠ” μ—†λ‹€.
    3. μ‚¬μš©μž Aκ°€ 리뷰λ₯Ό μ‚­μ œν•œλ‹€. μ‚¬μš©μž A의 회수 ν¬μΈνŠΈκ°€ κ³„μ‚°λ˜μ–΄ κΈ°λ‘λœλ‹€.

πŸ€” 회고

πŸͺ΅ ν…ŒμŠ€νŠΈ 컀버리지

λ‹€μŒμ— λΉ„μŠ·ν•œ ν”„λ‘œμ νŠΈλ₯Ό ν•  κ²½μš°μ—λŠ” ꡬ문, κ²°μ •, 쑰건 브랜치 ν…ŒμŠ€νŠΈ 컀버리지에 더 신경을 μ¨μ„œ 수치λ₯Ό λ†’μ—¬λ³΄λŠ” 것이 λͺ©ν‘œμž…λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈ 컀버리지가 μ ˆλŒ€μ μΈ 것은 μ•„λ‹ˆμ§€λ§Œ 높을 수둝 긍정적인 μ‹ ν˜Έμ΄κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

❗️ μ—λŸ¬ 핸듀링

보닀 κΌΌκΌΌν•œ μ—λŸ¬ ν•Έλ“€λ§μœΌλ‘œ λ‹€μ–‘ν•œ μ‘°κ±΄μ—μ„œ λ°œμƒν•˜λŠ” μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•΄μ„œ 더 robustν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ§Œλ“€κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.

☁️ 배포

.env λ“±μ˜ μ‹œν¬λ¦Ώ 파일 뢄리, p6spy ν•΄μ œ 등을 μ μš©ν•œ 배포 버전을 톡해 λ°”λ‘œ 배포할 수 μžˆλ„λ‘ λ§Œλ“€κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.

πŸ“ƒ μš”κ΅¬ 사항 뢄석에 λŒ€ν•΄μ„œ

μš”κ΅¬μ‚¬ν•­μ„ ν•΄μ„ν•˜λŠ” κ³Όμ •μ—μ„œ μ€‘μ˜μ μœΌλ‘œ 해석이 κ°€λŠ₯ν•œ 뢀뢄이 μžˆμ—ˆλŠ”λ°, 고객의 μž…μž₯μ—μ„œ 확인을 ν•œλ²ˆ 더 ν–ˆμ–΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€. 끝내고 λ‚˜λ‹ˆ 자의적으둜 ν•΄μ„ν•΄μ„œ 진행을 ν•œ 것 κ°™μ•„ 아쉬움이 λ‚¨μŠ΅λ‹ˆλ‹€.

μ•žμœΌλ‘œ 진행할 경우 μŠ€ν”„λ¦°νŠΈ λ‹¨μœ„λ‘œ μš”κ΅¬μ‚¬ν•­κ³Ό κΈ°λŠ₯이 μ •ν™•νžˆ μΌμΉ˜ν•˜λŠ”μ§€ ν™•μΈν•˜λŠ” 과정을 톡해 μ†Œν†΅μ„ ν•˜λ©° κ°œλ°œν•˜λŠ” 것이 λͺ©ν‘œμž…λ‹ˆλ‹€.

About

🧳 νŠΈλ¦¬ν”Œμ—¬ν–‰μž 클럽 λ§ˆμΌλ¦¬μ§€ μ„œλΉ„μŠ€

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published