์๋๋ถ์ยท๊ฑฐ์ฃผ์งยท์ ๊ณต ๊ธฐ๋ฐ ์ฅํ๊ธ ์๋ ์ถ์ฒ + D-day ์๋ฆผ ์๋น์ค์ Node.js API ์๋ฒ
์ฃผ๋ณ ํ์๋ค์ด ์กฐ๊ฑด์ ๋ง๋ ์ฅํ๊ธ ๊ณต๊ณ ๋ฅผ ๋์น๋ ๊ฒฝ์ฐ๋ฅผ ์์ฃผ ๋ชฉ๊ฒฉํ์ต๋๋ค. ์ ๋ณด๋ ์กด์ฌํ์ง๋ง ์ ๊ทผ์ด ๋ถํธํ๊ณ , ๋ง๊ฐ์ผ์ ์ธ์งํ์ง ๋ชปํด ํํ์ ๋ฐ์ง ๋ชปํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์์ต๋๋ค.
CEN์ ํ์ ๊ฐ์ธ ์ ๋ณด(์๋๋ถ์ยท๊ฑฐ์ฃผ์งยท์ ๊ณตยทํ์ )๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ง์ถค ์ฅํ๊ธ์ ์๋ ์ถ์ฒํ๊ณ , ๋ง๊ฐ D-day๋ฅผ ์ฑ ํธ์๋ก ์๋ ค์ฃผ๋ ์์คํฑ ์ฅํ๊ธ ์ ๋ณด ์๋น์ค์ ๋๋ค.
๊ณต๊ณต๋ฐ์ดํฐ API โ ์ฅํ๊ธ DB ์ ์ฌ โ ์ฌ์ฉ์ ์กฐ๊ฑด ํํฐ๋ง โ ๋ง์ถค ์ถ์ฒ + ํธ์ ์๋ฆผ
2024 ๋ฒ๊ฝํค ํด์ปคํค ์ถํ์ (ํ์ 6์ธ / Backend Developer ์ญํ )
- ํ์๊ฐ์ / ๋ก๊ทธ์ธ โ bcrypt ํด์ ๋น๋ฐ๋ฒํธ + JWT Access Token (7์ผ ์ ํจ)
- ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ โ ์ด๋ฆ + ์์ด๋ ๊ฒ์ฆ ํ ์ฌ๋ฐ๊ธ
- JWT ๋ฏธ๋ค์จ์ด โ ๋ชจ๋ ์ธ์ฆ ํ์ API์์ ํ ํฐ ๊ฒ์ฆ
- ์ต์ด ๋ก๊ทธ์ธ ์ ์๋๋ถ์ยทํ์ ยท๊ฑฐ์ฃผ์งยท์ ๊ณต ์ ๋ ฅ (์จ๋ณด๋ฉ)
- ์จ๋ณด๋ฉ ์๋ฃ ์ฌ๋ถ ์ฒดํฌ API ์ ๊ณต
- ํด์ํ๊ทธ(์กฐ๊ฑด ํํฐ) ์กฐํ API
- ์คํฌ๋ฉํ ์ฅํ๊ธ์ ๋ง๊ฐ์ผ์ ์๋ณ / ์ผ๋ณ ์บ๋ฆฐ๋๋ก ์กฐํ
- D-day ์๋ ๊ณ์ฐ (D-1, D-day, D+1 ํ์)
- ์ ์ฒญ ์ํ(status) ํจ๊ป ๋ฐํ
- 10์ด๋ง๋ค ์ ์ฅํ๊ธ ๊ณต๊ณ ๊ฐ์ง
- ์ ๊ท ๊ณต๊ณ ๋ฑ๋ก ์ ๋ฑ๋ก๋ ๋ชจ๋ iOS ๋๋ฐ์ด์ค์ ํธ์ ๋ฐ์ก
- Apple Push Notification ์๋น์ค(APN) ์ฐ๋
- Express + Spring Boot ํผ์ฌ ํ๊ฒฝ์์ ์ธ์ฆ ๋จ์ผํ
- JWT ๊ฒ์ฆ ํ Spring ๋ฐฑ์๋๋ก ์์ฒญ ํ๋ก์
- ์ธ์ฆ ๋ณต์ก๋๋ฅผ Express Gateway์์ ํตํฉ ์ฒ๋ฆฌ
[iOS ์ฑ (Swift)]
โ
โผ
[Express.js API Gateway :8001]
โโโ JWT ์ธ์ฆ ๋ฏธ๋ค์จ์ด
โโโ ์ง์ ์ฒ๋ฆฌ (User, Calendar, Scholarship)
โ โ
โ โผ
โ [MySQL DB]
โ โโโ users
โ โโโ scholarships
โ โโโ saves (์คํฌ๋ฉ)
โ โโโ Userdevice (ํธ์ ํ ํฐ)
โ
โโโ ํ๋ก์ (Spring Boot :?)
โ
โผ
[Spring Boot API]
(์ฅํ๊ธ ์ถ์ฒ ๋ก์ง, ์ํ๊ด๋ฆฌ ๋ฑ)
[node-cron / setInterval]
โโโ 10์ด๋ง๋ค ์ ๊ท ์ฅํ๊ธ ๊ฐ์ง โ APN ํธ์
ํต์ฌ ์ค๊ณ ๊ฒฐ์ โ API Gateway ํจํด: Express์ Spring Boot๊ฐ ํจ๊ป ์ฌ์ฉ๋๋ ํ๊ฒฝ์์, ๋ชจ๋ ์์ฒญ์ Express๊ฐ ๋จผ์ ๋ฐ์ JWT๋ฅผ ๊ฒ์ฆํ ๋ค Spring์ผ๋ก ํ๋ก์ํฉ๋๋ค. โ ์ธ์ฆ ๋ก์ง ์ค๋ณต ์ ๊ฑฐ, ๊ฐ๋ฐ ๋ณต์ก๋ ๊ฐ์, ๋ฐฐํฌ ๊ตฌ์กฐ ๋ช ํํ
| ๋ถ๋ฅ | ๊ธฐ์ |
|---|---|
| Runtime | Node.js |
| Framework | Express.js 4.18 |
| Auth | JWT (jsonwebtoken), bcrypt |
| DB | MySQL + Sequelize ORM |
| Push | Apple APNs (apn ๋ผ์ด๋ธ๋ฌ๋ฆฌ) |
| Proxy | express-http-proxy, http-proxy-middleware |
| Scheduler | node-cron, setInterval |
| Process | PM2 (ํ๋ก์ธ์ค ๊ด๋ฆฌ) |
| Dev | nodemon |
.
โโโ server.js # ์ง์
์ (Express ์ฑ ์ค์ + ํธ์ ์ค์ผ์ค๋ฌ)
โโโ routes/
โ โโโ user.js # ํ์๊ฐ์
/๋ก๊ทธ์ธ/์จ๋ณด๋ฉ/๋๋ฐ์ด์คํ ํฐ
โ โโโ scholarship.js # ์ฅํ๊ธ ๋ฐ์ดํฐ ์ ์ฌ
โ โโโ calendar.js # ์๋ณยท์ผ๋ณ ์บ๋ฆฐ๋ ์กฐํ
โ โโโ count.js # ์๋ณ ๋ง๊ฐ์ผ ์นด์ดํธ
โ โโโ proxy.js # Spring Boot ํ๋ก์ ๋ผ์ฐํฐ
โโโ controller/
โ โโโ user.js # ์ฌ์ฉ์ CRUD + JWT ๋ฐ๊ธ
โ โโโ scholarship.js # ์ฅํ๊ธ DB ์์ฑ
โ โโโ calendar.js # ์บ๋ฆฐ๋ ๋ก์ง + D-day ๊ณ์ฐ
โโโ middleware/
โ โโโ auth.js # JWT verifyToken ๋ฏธ๋ค์จ์ด
โโโ proxy/
โ โโโ proxy.js # Spring ๋ฐฑ์๋ ํ๋ก์ ํจ์
โโโ services/
โ โโโ notification.js # APN ํธ์ ์๋ฆผ ์๋น์ค
โโโ models/
โ โโโ user.js
โ โโโ scholarship.js
โ โโโ scrapped.js # ์คํฌ๋ฉ (saves ํ
์ด๋ธ)
โ โโโ userdevice.js # iOS ๋๋ฐ์ด์ค ํ ํฐ
โ โโโ count.js # ์ฅํ๊ธ ์นด์ดํธ (์ ๊ท ๊ฐ์ง์ฉ)
โโโ migrations/ # Sequelize ๋ง์ด๊ทธ๋ ์ด์
โโโ config/
โโโ config.json # DB ์ฐ๊ฒฐ ์ค์
users
โโโ userid (PK, STRING)
โโโ password (STRING, bcrypt ํด์)
โโโ name (STRING)
โโโ ranking (STRING) โ ์๋๋ถ์
โโโ grade (STRING) โ ํ์
โโโ region_city_province (STRING)
โโโ region_city_country_district (STRING)
โโโ major (STRING)
โโโ onboard (BOOLEAN)
scholarships (scholarships ํ
์ด๋ธ)
โโโ id (PK, AUTO_INCREMENT)
โโโ title, provider
โโโ start_date, end_date
โโโ amount, amount2
โโโ support_ranking, support_grade, support_major
โโโ support_city_province
โโโ support_target, support_target2, support_target3
โโโ description ~ description4
โโโ required_documents
โโโ site
saves (์คํฌ๋ฉ = Scrapped ๋ชจ๋ธ)
โโโ userid (STRING)
โโโ scholarship_id (INTEGER)
โโโ status (STRING)
Userdevice
โโโ userid (PK, STRING)
โโโ devicetoken (STRING)
CountScholarship (Count ๋ชจ๋ธ โ ์ ๊ท ์ฅํ๊ธ ๊ฐ์ง์ฉ)
โโโ id (PK, AUTO_INCREMENT)
โโโ count (INTEGER)
[server.js] setInterval(sendPushNotification, 10 * 1000)
โ
โผ
[services/notification.js]
1. DB์์ ์ ์ฒด ์ฅํ๊ธ ์(scholarships.length) ์กฐํ
2. CountScholarship ํ
์ด๋ธ์ ๋ง์ง๋ง count์ ๋น๊ต
3. ์ ๊ท ์ฅํ๊ธ ์์ผ๋ฉด โ count ์
๋ฐ์ดํธ
4. Userdevice ํ
์ด๋ธ์์ ๋ชจ๋ iOS ๋๋ฐ์ด์ค ํ ํฐ ์กฐํ
5. ๊ฐ ์ฌ์ฉ์์๊ฒ APN ํธ์ ๋ฐ์ก
โ "{name}๋ ์๋ก์ด ๊ณต๊ณ ๊ฐ ์ฌ๋ผ์์ต๋๋ค."
APN ์ค์ ๊ฐ (.env ํ์):
APN_KEY_PATHโ Apple์์ ๋ฐ๊ธํ.p8ํค ํ์ผ ๊ฒฝ๋กAPN_KEY_IDโ Key ID (10์๋ฆฌ)APN_TEAM_IDโ Apple Developer Team IDAPN_TOPICโ ์ฑ Bundle ID (์:com.yourcompany.CenApp)
ํด๋ผ์ด์ธํธ Express ์๋ฒ
โ โ
โโโ POST /user/login โโโโโโโโโถโ
โ โ bcrypt.compare(password, hash)
โ โ jwt.sign({ name, userid }, SECRET, { expiresIn: '7d' })
โโโโ { accesstoken } โโโโโโโโโโ
โ โ
โโโ GET /user/hashtag โโโโโโโโโถโ
โ Header: accesstoken: xxx โ jwt.verify(token, SECRET)
โ โ โ decoded.userid โ req.headers.userid
โโโโ { ranking, grade, ... } โโ
Express.js์ Spring Boot๋ฅผ ๋์์ ์ฌ์ฉํ๋ ํ ๊ตฌ์ฑ์์ ๊ฐ ์๋ฒ๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก JWT ์ธ์ฆ์ ์ฒ๋ฆฌํด์ผ ํ๋ ์ํฉ์ด ๋ฐ์ํ์ต๋๋ค. โ ์ธ์ฆ ๋ก์ง ์ค๋ณต, ์ฝ๋ ๋ณต์ก๋ ์ฆ๊ฐ, ํ ํฐ ๋ถ์ผ์น ์ค๋ฅ ๋น๋ฐ
์๋ก ๋ค๋ฅธ ๊ธฐ์ ์คํ(Node.js vs Java)์์ JWT ๋ฐ๊ธยท๊ฒ์ฆ ๋ก์ง์ ๊ฐ์ ๊ตฌํํ๋ค ๋ณด๋ ํ์ค์ด ๋ฌ๋ผ์ก์ต๋๋ค.
Express๋ฅผ API Gateway๋ก ์ง์ , ๋ชจ๋ ์์ฒญ์ Express๊ฐ ๋จผ์ ์์ ํ์ฌ JWT๋ฅผ ๊ฒ์ฆํ ํ Spring์ผ๋ก ํ๋ก์ํ๋ ๊ตฌ์กฐ๋ฅผ ์ฑํํ์ต๋๋ค.
Before:
ํด๋ผ์ด์ธํธ โ Express (JWT ๊ฒ์ฆ) โ Express API
ํด๋ผ์ด์ธํธ โ Spring (JWT ๊ฒ์ฆ) โ Spring API
After:
ํด๋ผ์ด์ธํธ โ Express (JWT ๊ฒ์ฆ 1ํ) โ Express API
โ Spring API (ํ๋ก์)
- ์ธ์ฆ ๋ก์ง ๋จ์ผํ โ ์ค๋ณต ์ฝ๋ ์ ๊ฑฐ
- JWT ๊ด๋ จ ์ค๋ฅ ๊ฐ์
- ๋ฐฐํฌ ๊ตฌ์กฐ ๋ช ํํ โ ํ ๊ฐ๋ฐ ์๋ ํฅ์
| ์ด๋ฆ | ์ฅ์ฑ์ |
| ์ด๋ฉ์ผ | [email protected] |
| GitHub | github.com/jjang6251 |
| ๊ธฐ๊ฐ | 2024.03 (ํด์ปคํค) |
| ์ญํ | Backend Developer (6์ธ ํ) |
๋ด๋น ๊ตฌํ:
middleware/auth.jsโ JWT Access/Refresh Token ์ค๊ณ ๋ฐ ๋ฏธ๋ค์จ์ดproxy/proxy.js+routes/proxy.jsโ API Gateway ํจํด ๊ตฌํservices/notification.jsโ APN ํธ์ ์๋ฆผ ์๋น์ค- DB ์คํค๋ง ์ค๊ณ ๋ฐ Sequelize ๋ง์ด๊ทธ๋ ์ด์
- GCP ์ธํ๋ผ ๊ตฌ์ฑ ๋ฐ ๋ฐฐํฌ
ISC License