νΈλ¦¬ν μ¬μ©μλ€μ΄ μ₯μμ 리뷰λ₯Ό μμ±ν λ ν¬μΈνΈλ₯Ό λΆμ¬νκ³ , μ 체/κ°μΈμ λν ν¬μΈνΈ λΆμ¬ νμ€ν 리μ, κ°μΈλ³ λμ ν¬μΈνΈλ₯Ό κ΄λ¦¬νκ³ μ ν©λλ€.
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λ‘ κ΅¬ννμμ΅λλ€. Web, Service, Repositoryλ‘ κ΅¬λΆνμμ΅λλ€.
-
ν΄λΌμ΄μΈνΈ β Controllerμ μ¬μ©λλ DTOμ, Controller β Serviceμ μ¬μ©λλ DTOλ₯Ό ꡬλΆνμ¬ κ΅¬ννμμ΅λλ€.
-
API controller ν΄λμ€λ
api
, service ν΄λμ€λservice
, repositoryλdao
, entityλdomain
ν¨ν€μ§μ κ΄μ¬μ¬μ λ°λΌ λͺ¨μλμμ΅λλ€. -
κ° κ³μΈ΅μμ μ¬μ©λλ DTOλ
dto
ν¨ν€μ§μ μ©λμ λ°λΌ λͺ¨μλμμ΅λλ€.- κ° DTOμλ
@NotNull
κ³Ό κ°μ μ΄λ Έν μ΄μ μ μ΄μ©ν validationμ΄ μ μ©λμ΄ μμ΅λλ€.
- κ° DTOμλ
-
ν΅μΌλ μμμ exception handlingμ μν΄
global.error
ν¨ν€μ§μ exception handler λ° exception, error code λ±μ λͺ¨μλμμ΅λλ€.
λ€μμ μꡬμ¬νμ λ°νμΌλ‘ μμ±ν ERDμ λλ€.
μ€ν€λ§λ src/main/resources/schema.sql
μ μμ±νμμ΅λλ€.
DDL Schema μμ± λ° unique & foreign constraint, index μ€μ μ νμμ΅λλ€.
DB Engineμ InnoDBλ‘ μ¬μ©ν©λλ€.
Endpointλ 리뷰 μμ±, μμ , μμ μ΄λ²€νΈλ₯Ό μ λ¬νλ /events
, μ¬μ©μ μ μ ν©κ³λ₯Ό νμΈνλ /points
, μ¬μ©μ μ μ κΈ°λ‘ λͺ©λ‘μ μ‘°ννλ /point-histories
λ‘ μ΄ 3κ°μ
λλ€. μ΄λ²€νΈμ μ’
λ₯κΉμ§ κ³μ°νλ©΄ 5κ°μ APIκ° λ©λλ€.
API λͺ μΈλ Spring REST Docsμ μ΄μ©ν΄ ν μ€νΈλ₯Ό ν΅ν΄ λ¬Έμννμ΅λλ€.
http://localhost:8080/docs/index.html λ‘ μ μνλ©΄ νμΈνμ€ μ μμ΅λλ€.
μΆκ°λ‘ PDFλ‘ μ μνμ¬ μ²¨λΆν©λλ€. αα ³α α ΅αα ³α― αα ³α―α α ₯αΈ αα ‘αα ΅α―α α ΅αα ΅ αα ₯αα ΅αα ³ API PDF
JUnit 5, Assertj, BDDMockito, Spring REST Docs λ° MockMvc λ±μ ν΅ν΄ μ λ ν μ€νΈ λ° ν΅ν© ν μ€νΈλ₯Ό μ§ννμμ΅λλ€.
μꡬμ¬ν λ¬Έμμ 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;
κ²°κ³Όλ λ€μκ³Ό κ°μ΅λλ€.
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λ² μ‘°κ±΄μ μ μ©ν μ μμ΅λλ€.
- κΈμ λ¨Όμ μμ νκ³ , μ΄ν μ¬μ§μ μμ νλ©΄ 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
ν΄μ λ±μ μ μ©ν λ°°ν¬ λ²μ μ ν΅ν΄ λ°λ‘ λ°°ν¬ν μ μλλ‘ λ§λ€κ³ μΆμ΅λλ€.
μꡬμ¬νμ ν΄μνλ κ³Όμ μμ μ€μμ μΌλ‘ ν΄μμ΄ κ°λ₯ν λΆλΆμ΄ μμλλ°, κ³ κ°μ μ μ₯μμ νμΈμ νλ² λ νμ΄μΌ νμ΅λλ€. λλ΄κ³ λλ μμμ μΌλ‘ ν΄μν΄μ μ§νμ ν κ² κ°μ μμ¬μμ΄ λ¨μ΅λλ€.
μμΌλ‘ μ§νν κ²½μ° μ€νλ¦°νΈ λ¨μλ‘ μꡬμ¬νκ³Ό κΈ°λ₯μ΄ μ νν μΌμΉνλμ§ νμΈνλ κ³Όμ μ ν΅ν΄ μν΅μ νλ©° κ°λ°νλ κ²μ΄ λͺ©νμ λλ€.