diff --git a/README.md b/README.md
index f2aae05..e92c17e 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,233 @@
-## ๐จ Tech Stack (Node.js Server)
+# โ๏ธ ์์ญํธ์ง (Soksak Letter)
-### Languages
-- **JavaScript (ES6+)**
+> ๋๋ฅผ ๋ง๋๋ ์ง๋ฌธ, ํ์ธ๊ณผ ๋๋๋ ์ต๋ช
ํธ์ง
+> ๊ณผ๋ํ ์๊ทน์ ์๋์, **์ฑ์ฐฐ๊ณผ ์ง์ ์ฑ ์๋ ์ฐ๊ฒฐ**์ ๋ค์ ๊บผ๋ด๋ ํธ์ง ์๋น์ค
-### Frameworks & Libraries
-- **Node.js**
-- **Express.js**
-- **JWT**
-- **Prisma**
-- **Swagger**
-- **dotenv**
+์ฌ์ฉ์ ์๋น์ค: https://www.soksak-letter.com/
-### Database
-- **MySQL**
+
-### Tools
-- **npm**
-- **ESLint**
-- **Prettier**
-- **Nodemon**
+
-### Version Control
-- **Git**
-- **GitHub**
+
+
----
+## ๐ต Team ์ฒ์ญ์ด
-## Convention
+### ํ๋ช
: ์ฒ์ญ์ด
-### Branch Convention
-- **๋ฐฐํฌ์ฉ**: main
-- **๊ฐ๋ฐ์ฉ**: develop
-- **์์
์ฉ**: ์ปค๋ฐ์ ํ/#์ด์๋ฒํธ-์ค๋ช
- - ์) feature/#3-์์
๋ก๊ทธ์ธAPI๊ตฌํ
+๊ฐ**์ฒ**๋(FE: 3๋ช
, BE: 3๋ช
) + **์ญ**์ค๋(PM, Designer, FE) => **์ฒ์ญ์ด**
+
+### R&R
+
+| ๋ถ์ผ | ์ด๋ฆ | ํฌ์ง์
|
+| ------ | ------------------------------------------------ | ------------------------------------------------ |
+| PM | ์ธ์ด/์์์ฐ | ํ๋ก์ ํธ ๋งค๋์ง, ์๊ตฌ์ฌํญ ์ ์, ์ผ์ /๋ฆฌ์คํฌ ๊ด๋ฆฌ |
+| Design | ์ฐ๋/์กฐ์ ์ด | ๋์์ธ ์์คํ
, UX/UI, ํ๋กํ ํ์
|
+| FE | ์ /๊น์ง์, ์๋/์ฐจ์๋ฆฐ, ์์/์์์ง, ์ ๋/๋จ์ง์ค | ์ฌ์ฉ์ ํ๋ก์ฐ ๊ตฌํ, API ์ฐ๋, ์ํ/์บ์ ๊ด๋ฆฌ |
+| BE | ํธ๋ฏธ/๊น์์ค, ๋๋ถ/์กฐ์ฐ์ค, ๊ท์/๋ฐ์น์ฃผ | API/DB ์ค๊ณ, ์ธ์ฆ/๋ณด์, ๋ฐฐํฌ/์ด์ |
+
+
+
+## ๐ฎ ์๋น์ค ๊ฐ์
+
+### ์๋น์ค ์ ์
+
+**๋๋ฅผ ๋ง๋๋ ์ง๋ฌธ, ํ์ธ๊ณผ ๋๋๋ ์ต๋ช
ํธ์ง ์๋น์ค**
+
+- ๋งค์ผ ๋์ฐฉํ๋ โ์ค๋์ ์ง๋ฌธโ์ ๋ตํ๋ฉฐ ์๊ฐ์ ์ ๋ฆฌํ๊ณ
+- ๋งค์นญ๋ ์๋์ **์ต๋ 10ํ** ํธ์ง๋ฅผ ์ฃผ๊ณ ๋ฐ์ผ๋ฉฐ
+- **3์ผ ๋ค ๋์๊ฒ ์ค๋ ํธ์ง(D+3)** ๋ก ๋ฆฌํ
์
๊ณผ ํ๋ณต ๊ฒฝํ์ ๋ง๋ ๋ค
+
+
+
+## ๐ฏ ๋ฌธ์ ์ ์
+
+### 1) ๋ฌธ์ ์ํฉ
+
+ํ๋์ AI/SNS ์ฌ์ฉ์๋ ์ํผยทSNSยทAI Slop ๋ฑ **๋ํ๋ฏผ์ฑ ์ ๋ณด ๊ณผ์**์ ๋
ธ์ถ๋์ด,
+๊ฐ์ ์ ์ผ๋ก ์ง์ณค์ ๋ ์ฆ๊ฐ์ ์ธ ์๊ทน์ ์์กดํ๊ฒ ๋ฉ๋๋ค.
+๊ทธ ๊ฒฐ๊ณผ โ์๊ฐํ ์๊ฐโ์ด ์ค์ด **์๊ธฐ ์ฑ์ฐฐ**์ด ์ด๋ ค์์ง๊ณ , ์ฅ๊ธฐ์ ์ผ๋ก ๋ฒ์์/๊ณ ๋ฆฝ๊ฐ์ด ๋์ ๋ฉ๋๋ค.
+
+### 2) ๋ฌธ์ ์์ธ
+
+- ๊ทผ๋ณธ ์์ธ: **๋ด๋ฉด์ ๋ถ์์ ์ฑ** + **๊ฑด๊ฐํ ์ฌ์ ๋ฅผ ์ํ ์ฐฝ๊ตฌ ๋ถ์ฌ**
+- ์ฌ์ด ํด๊ฒฐ(์ฆ๊ฐ ์๊ทน, AI ์์กด)๋ก ๊ณ ํต์ ํํผํ๋ ์ต๊ด ๊ฐํ
+- ๊ธฐ์กด ํด๊ฒฐ์ฑ
(์ผ๊ธฐ/๋ช
์ ์ฑ)์ ๋ถ๋ดยท์ง๋ฃจํจ์ผ๋ก **์ง์ ์ฌ์ฉ ์ ๋ ์คํจ**
+
+### 3) ๋ฌธ์ ์ํฅ
+
+- ์๊ธฐ ๊ฐ๊ดํ ๋ฅ๋ ฅ ์ ํ โ ๊ฐ์ ์๋ชจ ์ฆ๊ฐ
+- ์ธ๋ถ ์๊ทน ์์กด ๊ฐํ โ ์ฌ๋ฆฌ์ ํผ๋ก/๊ณ ๋ฆฝ๊ฐ ์
ํ
+- ์ฅ๊ธฐ์ ์ผ๋ก ์ ์ ์ ๊ฑด๊ฐ์ ๋ถ์ ์ ์ํฅ
+
+
+
+## ๐ก ๋ชฉ์ (Why) & ๋ฐฐ๊ฒฝ(Background)
+
+### ์๋น์ค ๋ชฉ์ (Why)
+
+**ํธ์ง์ ๊ฐ์น๋ฅผ ํตํด, ์ฌ์ฉ์์๊ฒ ์ฑ์ฐฐ๊ณผ ์ํต์ ๊ธฐํ๋ฅผ ์ ๊ณตํ์ฌ ์ฅ๊ธฐ์ ์ธ ์ฌ๋ฆฌ์ ์์ ๊ฐ์ ์ค๋ค.**
+
+### ๋ฐฐ๊ฒฝ(Background)
+
+| Fact | Insight |
+| ----------------------------------------- | -------------------------------------------------------------- |
+| ๋์งํธ ๋ํก์ค/๋ํ๋ฏผ ๋ํก์ค ํธ๋ ๋ ํ์ฐ | ์ฌ์ฉ์๋ ์๊ทน์ ์
์ํ์ ์ง์ณค๊ณ , **์ต๊ด ๊ต์ ํ ๊ฒฝํ**์ ์ํ๋ค |
+| SNS ์ฌ์ฉ ์ฆ๊ฐ โ ๋น๊ต ์ฌํ๋ก ์ธํ ๊ณ ๋ฆฝ๊ฐ | ์ธ๋ก์์ ์ฐ๊ฒฐ์ ๋ถ์ฌ๊ฐ ์๋๋ผ **์ฐ๊ฒฐ์ ํ์ง ๋ฌธ์ **๋ค |
+| ์ฌ๋ฆฌ์๋ด ์์ฅ ์ฑ์ฅ, ์ฌํ์ ๊ณ ๋ฆฝ ์ด์ ํ๋ | ํฐ ๋น์ฉ/์๊ฐ ์์ด ์ผ์ ์์์ **์๋ฐฉ์ ์๊ธฐ๊ด๋ฆฌ** ๋์ฆ๊ฐ ์๋ค |
+
+์ฐธ๊ณ (์์): ์์ธ์ โ์ฌํ์ ๊ณ ๋ฆฝโ ์ ์ฑ
์๋ฃ: [https://www.seoul.go.kr/policy/view.do?id=1056&lan=KO](https://www.seoul.go.kr/policy/view.do?id=1056&lan=KO)
+
+
+
+## ๐ ์ฃผ์ ์ฌ์ฉ์(Target User)
+
+- ๋ํ์
+- 20๋ ์ง์ฅ์ธ
+- 20๋ AI/SNS ํค๋น ์ ์
+
+
+
+## ๐งญ User Journey Map
+
+> โ์ ์
โ ํ์ โ ๋งค์นญ/์ํต โ ์ข
๋ฃ/ํผ๋๋ฐฑ โ ์ฌ๋ฐฉ๋ฌธ/ํ์ฅโ์ ๊ฒฝํ์ ํ ์ฅ์ผ๋ก ์ค๊ณ
+
+| ๋จ๊ณ | 1. ์ ์
๋ฐ ์จ๋ณด๋ฉ | 2. ํ์ | 3. ๋งค์นญ ๋ฐ ์ํต | 4. ์ข
๋ฃ ๋ฐ ํผ๋๋ฐฑ | 5. ์ฌ๋ฐฉ๋ฌธ ๋ฐ ํ์ฅ |
+| ----------- | ------------------------------------------------ | --------------------- | ------------------------- | -------------------------- | ---------------------------------- |
+| ์ฌ์ฉ์ ๋ชฉํ | ๋น ๋ฅด๊ฒ ๊ฐ์
ํ๊ณ ๊ฐ์น ํ์ธ | ์ค๋์ ์ง๋ฌธ ํ์ธ | ํฐํคํ์นด ๋๋ ๋ํ | ๊น๋ํ ๋ง๋ฌด๋ฆฌ/ํ๊ฐ | ๊ธฐ๋ก ํ์ธ/์ถ๊ฐ ์์ฑ |
+| ์ฃผ์ ํ๋ | ์์
๋ก๊ทธ์ธ โ ํ๋กํ โ **D+3 ํธ์ง(ํ์)** โ ์๋ฆผ | ์ง๋ฌธ ํ์ธ โ ๋ต๋ณ ์์ฑ | ๋ฐ์กโ๋งค์นญโ๊ตํ(์ต๋ 10ํ) | ์ข
๋ฃ(10ํ/48h) โ ์จ๋/ํ๊ทธ | **3์ผ ์ ํธ์ง ๋์ฐฉ** โ ์ฃผ๊ฐ ๋ฆฌํฌํธ |
+| ํฐ์นํฌ์ธํธ | ๋๋ฉ/์จ๋ณด๋ฉ/OS ์๋ฆผ | ํ(์ค๋์ ์ง๋ฌธ ์นด๋) | ์ฑํ
๋ฐฉ/์นด์ดํฐ | ๋ฆฌ๋ทฐ ๋ชจ๋ฌ/์ข
๋ฃ ํ์ด์ง | ๋ณด๊ดํจ/๋ฆฌํฌํธ/DatePicker |
+| ๊ธฐํ์์ธ | Lock-in(๋ฏธ๋ ๋ณด์) | ํฌ์์ฑ(24h) | ํ์ง๊ด๋ฆฌ(๋ต์ฅ๋ฅ /์จ๋) | ๋ฐ์ดํฐ ์ ์ํ | ์ ๋ฃํ ํ์ฅ |
+
+
+
+
+
+
+
+## ๐งฉ ํต์ฌ ๊ธฐ๋ฅ
+
+- **์ค๋์ ์ง๋ฌธ(24์๊ฐ)**
+ ๋งค์ผ ์ง๋ฌธ ๊ฐฑ์ / ์นด์ดํธ๋ค์ด / ๋ต๋ณ ์ง์
+
+- **์ต๋ช
๋งค์นญ & ํธ์ง ๊ตํ(์ต๋ 10ํ, 48์๊ฐ)**
+ ๋ํ ํ์ ์นด์ดํธ, ๋ง๋ฃ ์ฒ๋ฆฌ
+
+- **D+3 ๋์๊ฒ ์ฐ๋ ํธ์ง(์จ๋ณด๋ฉ ํ์)**
+ โ๋ฏธ๋์ ๋โ์๊ฒ ๋ณด๋ด๋ ์์ฝ ํธ์ง, ์๋ฆผ์ผ๋ก ํ์
+
+- **์ข
๋ฃ ๋ฆฌ๋ทฐ(์จ๋ ์ฌ๋ผ์ด๋ + ํ๊ธฐ ํ๊ทธ)**
+ ๊ฒฝํ ๋ฐ์ดํฐ ์์ง โ ๋ค์ ๋งค์นญ ํ์ง ๊ฐ์
+
+- **์ฃผ๊ฐ ๋ฆฌํฌํธ**
+ ์ผ์ฃผ์ผ ๊ธฐ๋ก ์์ฝ/๊ฐ์ ๋ถํฌ/ํค์๋ ์ธ์ฌ์ดํธ ์ ๊ณต
+
+- **์น๊ตฌ ๊ธฐ๋ฅ / ํ
๋ง ๋ฐฐ๊ฒฝ / ํฐํธ ์ต์
/ ์์ฝ ํธ์ง ํ์ฅ**
+
+
+
+## ๐ System Architecture
+
+
+
+
+
+
+## ๐๏ธ ERD
+
+
+
+
+
+
+## **๐ป Technology**
+
+### Web
+
+- 
+ 
+ 
+ 
+ 
+
+### Server
+
+- 
+ 
+ 
+- 
+- 
+ 
+ 
+
+### Co-working Tool
+
+- 
+ 
+
+
+
+## **๐พ ๊ธฐ์ ์คํ ์ ์ ์ด์ **
+
+### **๐ย Web ์คํ ์ ์ ์ด์ **
+
+- React + Vite
+ ์์ญํธ์ง๋ ๋ชจ๋ฐ์ผ 375px ๊ณ ์ UI ๊ธฐ๋ฐ์ SPA ์๋น์ค๋ก, SSR์ด ํ์์ ์ธ ๊ตฌ์กฐ๋ ์๋๋๋ค. ๋น ๋ฅธ ๊ฐ๋ฐ ์๋์ ๊ฐ๋ฒผ์ด ๋ฒ๋ค ํ๊ฒฝ์ ์ ๊ณตํ๋ Vite ๊ธฐ๋ฐ React ๊ตฌ์กฐ๋ฅผ ์ฑํํ์ฌ ๊ฐ๋ฐ ํจ์จ์ฑ๊ณผ ๋น๋ ์๋๋ฅผ ๋์์ ํ๋ณดํ์์ต๋๋ค.
+
+- TypeScript
+ ํธ์ง, ๋งค์นญ, ๋ฆฌํฌํธ ๋ฑ ๋๋ฉ์ธ์ด ๋ช
ํํ๊ณ API ์๋ต ๊ตฌ์กฐ๊ฐ ์ค์ํ ์๋น์ค ํน์ฑ์ ํ์
์์ ์ฑ์ด ์ค์ํฉ๋๋ค. TypeScript๋ฅผ ํตํด ํ์
์ ์๋๋ฅผ ๋ช
ํํ ํ๊ณ , ๋ฆฌํฉํ ๋ง ๊ณผ์ ์์ ๋ฐ์ํ ์ ์๋ ์ค๋ฅ๋ฅผ ์ฌ์ ์ ๋ฐฉ์งํ๊ณ ์ ์ ์ ํ์์ต๋๋ค.
+
+- TanStack Query
+ ์๋ฒ ๋ฐ์ดํฐ์ ํด๋ผ์ด์ธํธ ์ํ๋ฅผ ๋ถ๋ฆฌํ์ฌ ๊ด๋ฆฌํ๊ธฐ ์ํด TanStack Query๋ฅผ ๋์
ํ์์ต๋๋ค. ์บ์ฑ, ์๋ refetch, ๋ก๋ฉ/์๋ฌ ์ํ ๊ด๋ฆฌ ๊ธฐ๋ฅ์ ํ์ฉํ์ฌ API ์ค์ฌ ๊ตฌ์กฐ์์ ์์ ์ ์ธ ๋ฐ์ดํฐ ํ๋ฆ์ ์ ์งํ๊ณ ์ ํ์์ต๋๋ค.
+
+- Zustand
+ ์ ์ญ ์ํ๋ฅผ ์ต์ํ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ํด ๊ฒฝ๋ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ Zustand๋ฅผ ์ฑํํ์์ต๋๋ค. ๋ถํ์ํ ๋ณด์ผ๋ฌํ๋ ์ดํธ ์์ด ์ง๊ด์ ์ธ store ๊ตฌ์กฐ๋ฅผ ์ ์งํ ์ ์์ผ๋ฉฐ, ์ธ์ฆ ์ํ๋ ๋ชจ๋ฌ ๊ด๋ฆฌ ๋ฑ UI ์ค์ฌ ์ ์ญ ์ํ์ ์ ํฉํ๋ค๊ณ ํ๋จํ์์ต๋๋ค.
+
+- Tailwind CSS
+ Figma ๊ธฐ๋ฐ ๋์์ธ ์์คํ
์ ๋น ๋ฅด๊ฒ ๊ตฌํํ๊ธฐ ์ํด Tailwind CSS๋ฅผ ์ฌ์ฉํ์์ต๋๋ค. ์ ํธ๋ฆฌํฐ ํด๋์ค ๊ธฐ๋ฐ ์คํ์ผ๋ง์ ํตํด ์ผ๊ด๋ ๋์์ธ์ ์ ์งํ๋ฉด์๋ ๊ฐ๋ฐ ์๋๋ฅผ ๋์ผ ์ ์์ด ์ ์ ํ์์ต๋๋ค.
+
+- Framer Motion
+ ํธ์ง ์ ํ, ๋ชจ๋ฌ ๋ฑ์ฅ ๋ฑ ๊ฐ์ฑ์ ์ธ ์ธํฐ๋์
์ด ์ค์ํ ์๋น์ค ํน์ฑ์ ๊ณ ๋ คํ์ฌ Framer Motion์ ๋์
ํ์์ต๋๋ค. ์ง๊ด์ ์ธ API์ ๋ถ๋๋ฌ์ด ์ ๋๋ฉ์ด์
๊ตฌํ์ด ๊ฐ๋ฅํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐํํ ์ ์์์ต๋๋ค.
+
+
+
+### **๐ Server ์คํ ์ ์ ์ด์ **
+
+- Node.js + Express
+ ์์ญํธ์ง๋ REST ๊ธฐ๋ฐ API ์๋ฒ ๊ตฌ์กฐ๋ก ์ค๊ณ๋์์ต๋๋ค. ๋น ๋ฅธ ๊ฐ๋ฐ๊ณผ ์ ์ฐํ ๋ผ์ฐํ
๊ตฌ์ฑ์ด ๊ฐ๋ฅํ Node.js์ Express๋ฅผ ํ์ฉํ์ฌ ํ๋ก ํธ์๋์์ ํ์
ํจ์จ์ ๋์ด๊ณ ์ ํ์์ต๋๋ค.
+
+- MySQL
+ ํธ์ง, ์ ์ , ๋งค์นญ, ๋ฆฌ๋ทฐ ๋ฑ ๊ด๊ณํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๊ฐ ๋ช
ํํ ์๋น์ค์ด๊ธฐ ๋๋ฌธ์ ํธ๋์ญ์
๊ณผ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํ๋ RDBMS์ธ MySQL์ ์ฑํํ์์ต๋๋ค. ๋ฐ์ดํฐ์ ์์ ์ฑ๊ณผ ์ผ๊ด์ฑ์ ์ ์งํ๋ ๋ฐ ์ค์ ์ ๋์์ต๋๋ค.
+
+- Redis
+ ๋งค์นญ ๋๊ธฐ์ด ์ฒ๋ฆฌ ๋ฐ ์บ์ฑ๊ณผ ๊ฐ์ด ๋น ๋ฅธ ์๋ต์ด ํ์ํ ์์ญ์ ์ต์ ํํ๊ธฐ ์ํด Redis๋ฅผ ์ฌ์ฉํ์์ต๋๋ค. ์ด๋ฅผ ํตํด ์๋ฒ ๋ถํ๋ฅผ ์ค์ด๊ณ ์๋ต ์๋๋ฅผ ๊ฐ์ ํ๊ณ ์ ํ์์ต๋๋ค.
+
+- JWT
+ ๋ฌด์ํ ์ธ์ฆ ๊ตฌ์กฐ๋ฅผ ์ ์งํ๊ธฐ ์ํด JWT ๊ธฐ๋ฐ ์ธ์ฆ ๋ฐฉ์์ ์ ์ฉํ์์ต๋๋ค. ์๋ฒ ํ์ฅ์ฑ๊ณผ ์ธ์ฆ ์ฒ๋ฆฌ์ ๋จ์ํ๋ฅผ ๊ณ ๋ คํ์ฌ ํด๋น ๋ฐฉ์์ ์ฑํํ์์ต๋๋ค.
+
+
+
+## ๐ Convention
+
+### Commit Convention
+1. ์ปค๋ฐ ์ ํ
+ - Feat
+ - Fix
+ - Remove
+ - Refactor
+ - Style
+ - Test
+ - Chore
+ - docs
+
+2. ์ปค๋ฐ ๋ฉ์์ง
+- `Feat: ์ฌ์ฉ์ ๋ก๊ทธ์ธ API ๊ตฌํ (#9)`
+
+3. ๊ท์น
+- ์ ๋ชฉ 50์ ์ด๋ด
+- ์ ๋ชฉ ๋ ๋ง์นจํ ๊ธ์ง
+- ๋ณธ๋ฌธ์ ๋ฌด์/์ ์์ฑ
### Coding Convention
- **ํ์ผ๋ช
**: kebab-case
@@ -59,13 +254,11 @@
- PUT / PATCH โ updateProfile()
- DELETE โ deleteUser()
----
### Issue Convention
- ์์ | **[์ปค๋ฐ์ ํ] ์ด์ ๋ด์ฉ**
- ์์ | **[FEAT] ์ฌ์ฉ์ ๋ก๊ทธ์ธ API ๊ตฌํ**
----
### PR Convention
1. PR ํ์
@@ -80,51 +273,26 @@
4. ํ
์คํธ ๊ฒฐ๊ณผ
- API ํ
์คํธ ๊ฒฐ๊ณผ ๋๋ Swagger ์บก์ฒ
----
-### Commit Convention
-1. ์ปค๋ฐ ์ ํ
- - Feat
- - Fix
- - Remove
- - Refactor
- - Style
- - Test
- - Chore
- - docs
+## ๐ฌ Branch Strategy
-2. ์ปค๋ฐ ๋ฉ์์ง
-- `Feat: ์ฌ์ฉ์ ๋ก๊ทธ์ธ API ๊ตฌํ (#9)`
+### main
-3. ๊ท์น
-- ์ ๋ชฉ 50์ ์ด๋ด
-- ์ ๋ชฉ ๋ ๋ง์นจํ ๊ธ์ง
-- ๋ณธ๋ฌธ์ ๋ฌด์/์ ์์ฑ
+- ์ค์ ์๋น์ค์ ๋ฐฐํฌ๋๋ ๋ธ๋์น
+- ์ง์ ๊ฐ๋ฐํ์ง ์์ผ๋ฉฐ, dev ๋ธ๋์น๋ฅผ ํตํด ๋ณํฉ
+
+### dev
+
+- main์์ ๋ถ๊ธฐ๋๋ ๊ฐ๋ฐ ํตํฉ ๋ธ๋์น
+- feature ๋ธ๋์น์์ ์์
ํ ๊ธฐ๋ฅ์ ๋ณํฉ
+- ์ถฉ๋ถํ ํ
์คํธ ํ main์ผ๋ก ๋ฐฐํฌ
+
+### fix/#์ด์๋ฒํธ-๊ธฐ๋ฅ๋ช
+
+- ๋ฒ๊ทธ ์์ ๋ธ๋์น
+- dev์์ ๋ถ๊ธฐํ์ฌ ์์ ํ dev๋ก ๋ณํฉ
+- ๊ธฐ๋ฅ ๊ตฌํ ๋ธ๋์น๊ฐ dev์ ๋ณํฉ๋์ด ์๋์ง ํ์ธ ํ ์์
+
+
----
-
-## SSH Tunneling
-
-### 1. ํฐ๋๋ง ์คํ
-ํฐ๋ฏธ๋์ ์ด๊ณ ์๋ ๋ช
๋ น์ด๋ฅผ ์
๋ ฅํ ๋ค prisma ์ฌ์ฉ
-
-```
-ssh -fN -i "public ํค ์์น" - L 3307:(DB๊ฐ ์๋ ์๋ธ๋ท private ip):3306 ubuntu@(๋ฐฑ์๋ ์๋ฒ public ip)
-```
-
-โฃ ๐.github
- โฃ ๐prisma
- โ โ ๐schema.prisma
- โฃ ๐src
- โ โฃ ๐controllers
- โ โฃ ๐errors
- โ โฃ ๐repositories
- โ โฃ ๐services
- โ โฃ ๐utils
- โ โฃ ๐db.config.js
- โ โ ๐index.js
- โฃ ๐.gitignore
- โฃ ๐package-lock.json
- โฃ ๐package.json
- โฃ ๐README.md
- โ ๐swagger.config.js
+---
\ No newline at end of file
diff --git a/src/repositories/letter.repository.js b/src/repositories/letter.repository.js
index c695160..628b7b3 100644
--- a/src/repositories/letter.repository.js
+++ b/src/repositories/letter.repository.js
@@ -91,7 +91,7 @@ export const getLetterDetail = async (id) => {
letter: {
id: letter.id,
title: letter.title,
- content: letter.title,
+ content: letter.content,
deliveredAt: letter.deliveredAt,
question: letter?.question.content,
design: {
@@ -467,4 +467,112 @@ export const selectQueuedLetter = async () => {
})
return letter;
-}
\ No newline at end of file
+}
+
+// ์ธ์
๋ณ ํธ์ง ๊ฐ์ ์กฐํ
+export const countLettersBySessionIdAndUserId = async ({ sessionId, userId, letterType }) => {
+ return await prisma.letter.count({
+ where: {
+ letterType,
+ sessionId: sessionId,
+ OR: [
+ { receiverUserId: userId },
+ { senderUserId: userId }
+ ]
+ }
+ });
+};
+
+// ์ธ์
๋ณ ์ฝ์ง ์์ ํธ์ง ๊ฐ์ ์กฐํ
+export const countUnreadLettersBySessionIdAndUserId = async ({ sessionId, userId, letterType }) => {
+ return await prisma.letter.count({
+ where: {
+ letterType,
+ sessionId: sessionId,
+ receiverUserId: userId,
+ readAt: null,
+ }
+ });
+};
+
+// ์ธ์
๋ณ ๋ฐ์ ํธ์ง ์กฐํ
+export const findReceivedLettersBySessionIdAndUserId = async ({ sessionId, userId, letterType }) => {
+ return await prisma.letter.findMany({
+ where: {
+ receiverUserId: userId,
+ sessionId: sessionId,
+ letterType,
+ },
+ orderBy: [{ deliveredAt: "desc" }, { createdAt: "desc" }],
+ select: {
+ id: true,
+ title: true,
+ deliveredAt: true,
+ readAt: true,
+ createdAt: true,
+ question: {
+ select: {
+ content: true
+ }
+ },
+ design: {
+ select: {
+ paper: {
+ select: {
+ id: true,
+ color: true,
+ }
+ },
+ stamp: {
+ select: {
+ id: true,
+ name: true,
+ assetUrl: true
+ }
+ },
+ },
+ },
+ },
+ });
+};
+
+// ์ธ์
๋ณ ๋ณด๋ธ ํธ์ง ์กฐํ
+export const findSentLettersBySessionIdAndUserId = async ({ sessionId, userId, letterType }) => {
+ return await prisma.letter.findMany({
+ where: {
+ senderUserId: userId,
+ sessionId: sessionId,
+ letterType,
+ },
+ orderBy: [{ deliveredAt: "desc" }, { createdAt: "desc" }],
+ select: {
+ id: true,
+ title: true,
+ deliveredAt: true,
+ readAt: true,
+ createdAt: true,
+ question: {
+ select: {
+ content: true
+ }
+ },
+ design: {
+ select: {
+ paper: {
+ select: {
+ id: true,
+ color: true,
+ }
+ },
+ stamp: {
+ select: {
+ id: true,
+ name: true,
+ assetUrl: true
+ }
+ },
+ },
+ },
+ },
+ });
+};
\ No newline at end of file
diff --git a/src/repositories/session.repository.js b/src/repositories/session.repository.js
index 86af207..01ef6f5 100644
--- a/src/repositories/session.repository.js
+++ b/src/repositories/session.repository.js
@@ -202,3 +202,24 @@ export const countMatchingSessionByUserId = async (userId) => {
return count;
};
+
+// ์ธ์
ID์ ์ฌ์ฉ์ ID๋ก ์ธ์
์กฐํ (์ฐธ๊ฐ์ ํฌํจ)
+export const findMatchingSessionBySessionIdAndUserId = async ({ sessionId, userId }) => {
+ return await prisma.matchingSession.findFirst({
+ where: {
+ id: sessionId,
+ participants: {
+ some: {
+ userId: userId
+ }
+ }
+ },
+ include: {
+ participants: {
+ select: {
+ userId: true
+ }
+ }
+ }
+ });
+};
\ No newline at end of file
diff --git a/src/repositories/user.repository.js b/src/repositories/user.repository.js
index 3b6f051..1583579 100644
--- a/src/repositories/user.repository.js
+++ b/src/repositories/user.repository.js
@@ -591,6 +591,11 @@ export const findSelfLetters = async ({ userId, letterType }) => {
createdAt: true,
deliveredAt: true,
questionId: true,
+ question: {
+ select: {
+ content: true
+ }
+ },
design: {
select: {
paperId: true,
diff --git a/src/services/mailbox.service.js b/src/services/mailbox.service.js
index 1e96796..0f17cf4 100644
--- a/src/services/mailbox.service.js
+++ b/src/services/mailbox.service.js
@@ -6,8 +6,15 @@ import {
import { LETTER_TYPE_ANON, LETTER_TYPE_SELF, makePreview } from "../utils/user.util.js";
import { findFriendById } from "../repositories/friend.repository.js";
import { NotFriendError } from "../errors/friend.error.js";
-import { getFriendLetters, getMyLettersWithFriend } from "../repositories/letter.repository.js";
-import { prisma } from "../configs/db.config.js";
+import {
+ getFriendLetters,
+ getMyLettersWithFriend,
+ countLettersBySessionIdAndUserId,
+ countUnreadLettersBySessionIdAndUserId,
+ findReceivedLettersBySessionIdAndUserId,
+ findSentLettersBySessionIdAndUserId,
+} from "../repositories/letter.repository.js";
+import { findMatchingSessionBySessionIdAndUserId } from "../repositories/session.repository.js";
import { MailboxInvalidSessionIdError } from "../errors/mailbox.error.js";
// ------------------------------
@@ -62,15 +69,10 @@ export const getAnonymousThreads = async (userId) => {
// ๊ฐ ์ธ์
๋ณ ํธ์ง ๊ฐ์ ์กฐํ (sessionId ๊ธฐ์ค, ๊ฐ๋ณ ์กฐํ)
const letterCounts = await Promise.all(
sessionIds.map(async (sessionId) => {
- const count = await prisma.letter.count({
- where: {
- letterType: LETTER_TYPE_ANON,
- sessionId: sessionId,
- OR: [
- { receiverUserId: userId },
- { senderUserId: userId }
- ]
- }
+ const count = await countLettersBySessionIdAndUserId({
+ sessionId,
+ userId,
+ letterType: LETTER_TYPE_ANON,
});
return [sessionId, count];
})
@@ -80,13 +82,10 @@ export const getAnonymousThreads = async (userId) => {
// ๊ฐ ์ธ์
๋ณ ์ฝ์ง ์์ ํธ์ง ์ฒดํฌ (๋ฐ์ ํธ์ง๋ง ์ฒดํฌ)
const unreadChecks = await Promise.all(
sessionIds.map(async (sessionId) => {
- const unreadCount = await prisma.letter.count({
- where: {
- letterType: LETTER_TYPE_ANON,
- sessionId: sessionId,
- receiverUserId: userId,
- readAt: null, // ์ฝ์ง ์์ ํธ์ง
- }
+ const unreadCount = await countUnreadLettersBySessionIdAndUserId({
+ sessionId,
+ userId,
+ letterType: LETTER_TYPE_ANON,
});
return [sessionId, unreadCount > 0]; // ํ๋๋ผ๋ ์์ผ๋ฉด true
})
@@ -129,22 +128,9 @@ export const getAnonymousThreadLetters = async (userId, sessionIdRaw) => {
const sessionId = Number(sessionIdRaw);
// ์ธ์
์ฐธ๊ฐ์ ์กฐํํ์ฌ ๊ถํ ํ์ธ
- const session = await prisma.matchingSession.findFirst({
- where: {
- id: sessionId,
- participants: {
- some: {
- userId: userId // ํ์ฌ ์ฌ์ฉ์๊ฐ ์ฐธ๊ฐ์์ธ ์ธ์
๋ง
- }
- }
- },
- include: {
- participants: {
- select: {
- userId: true
- }
- }
- }
+ const session = await findMatchingSessionBySessionIdAndUserId({
+ sessionId,
+ userId,
});
if (!session) {
@@ -152,81 +138,17 @@ export const getAnonymousThreadLetters = async (userId, sessionIdRaw) => {
}
// ๋ฐ์ ํธ์ง ์กฐํ (sessionId๋ก ํํฐ๋ง)
- const receivedLetters = await prisma.letter.findMany({
- where: {
- receiverUserId: userId,
- sessionId: sessionId,
- letterType: LETTER_TYPE_ANON,
- },
- orderBy: [{ deliveredAt: "desc" }, { createdAt: "desc" }],
- select: {
- id: true,
- title: true,
- deliveredAt: true,
- readAt: true,
- createdAt: true,
- question: {
- select: {
- content: true
- }
- },
- design: {
- select: {
- paper: {
- select: {
- id: true,
- color: true,
- }
- },
- stamp: {
- select: {
- id: true,
- name: true,
- assetUrl: true
- }
- },
- },
- },
- },
+ const receivedLetters = await findReceivedLettersBySessionIdAndUserId({
+ sessionId,
+ userId,
+ letterType: LETTER_TYPE_ANON,
});
// ๋ณด๋ธ ํธ์ง ์กฐํ (sessionId๋ก ํํฐ๋ง)
- const sentLetters = await prisma.letter.findMany({
- where: {
- senderUserId: userId,
- sessionId: sessionId,
- letterType: LETTER_TYPE_ANON,
- },
- orderBy: [{ deliveredAt: "desc" }, { createdAt: "desc" }],
- select: {
- id: true,
- title: true,
- deliveredAt: true,
- readAt: true,
- createdAt: true,
- question: {
- select: {
- content: true
- }
- },
- design: {
- select: {
- paper: {
- select: {
- id: true,
- color: true,
- }
- },
- stamp: {
- select: {
- id: true,
- name: true,
- assetUrl: true
- }
- },
- },
- },
- },
+ const sentLetters = await findSentLettersBySessionIdAndUserId({
+ sessionId,
+ userId,
+ letterType: LETTER_TYPE_ANON,
});
// firstQuestion: ๋ฐ์ ํธ์ง์ ์ฒซ ๋ฒ์งธ ์ง๋ฌธ ์ฐ์ , ์์ผ๋ฉด ๋ณด๋ธ ํธ์ง์ ์ฒซ ๋ฒ์งธ ์ง๋ฌธ
@@ -296,7 +218,7 @@ export const getSelfMailbox = async (userId) => {
id: l.id,
title: l.title,
createdAt: l.createdAt ?? null,
- questionId: l.questionId ?? null,
+ questionTitle: l.question?.content ?? null,
paperId: l.design?.paperId ?? null,
stampId: l.design?.stampId ?? null,
stampUrl: l.design?.stamp?.assetUrl ?? null,
diff --git a/src/swagger/mailbox.swagger.js b/src/swagger/mailbox.swagger.js
index 44a1ee3..3b1ffa5 100644
--- a/src/swagger/mailbox.swagger.js
+++ b/src/swagger/mailbox.swagger.js
@@ -522,9 +522,10 @@
* type: string
* format: date-time
* nullable: true
- * questionId:
- * type: integer
+ * questionTitle:
+ * type: string
* nullable: true
+ * description: ์ง๋ฌธ ๋ด์ฉ
* paperId:
* type: integer
* nullable: true