๐Ÿ•Š๏ธํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค ๋ฐ๋ธŒ์ฝ”์Šค/TIL

[๋ฐ๋ธŒ์ฝ”์Šค] Express ๋ผ์šฐํ„ฐ ๋ชจ๋“ˆํ™”์™€ userId๋ฅผ ํ™œ์šฉํ•œ API ๊ตฌ์กฐ ๊ฐœ์„ 

ํ‚ํ‚์ž‰ 2025. 4. 23. 23:02

์–ด์ œ์— ์ด์–ด router์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ๊ฐœ์„  ์ž‘์—…์„ ์ง„ํ–‰ํ•˜์˜€๋‹ค. ๊ธฐ์กด์— ๊ตฌํ˜„ํ–ˆ๋˜ API๋ฅผ ๋ชจ๋“ˆํ™”ํ•˜๊ณ , ๊ตฌ์กฐํ™”ํ•˜๋ฉด์„œ ๋” ํšจ์œจ์ ์ธ ์ฝ”๋“œ ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•์„ ๋ฐฐ์› ๋‹ค


โญ์„œ๋ฒ„์™€ ๋ผ์šฐํ„ฐ

์ด์ „์—๋„ ์ •๋ฆฌํ–ˆ์ง€๋งŒ ํ•œ ๋ฒˆ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ์ •๋ฆฌํ•˜๊ณ  ๋„˜์–ด๊ฐ€๊ธฐ

์„œ๋ฒ„

ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ HTTP ์š”์ฒญ์„ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฃผ์ฒด

Express์—์„œ app ๊ฐ์ฒด๊ฐ€ ์„œ๋ฒ„์˜ ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๋ฉฐ, ๋ชจ๋“  ์š”์ฒญ์€ ์ด ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌ๋œ๋‹ค.

const express = require("express");
const app = express();

app.listen(7773);

 

๋ผ์šฐํ„ฐ

ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ URL์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ๊ฒฝ๋กœ๋กœ ์š”์ฒญ์„ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• .

์‰ฝ๊ฒŒ ๋งํ•ด, ์š”์ฒญ์ด ๋“ค์–ด์™”์„ ๋•Œ "์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ์ด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ• ์ง€" ๊ฒฐ์ •ํ•œ๋‹ค!

const userRouter = require("./routes/user-demo");
const channelRouter = require("./routes/channel-demo");

app.use("/", userRouter);
app.use("/channels", channelRouter);
  • app.user("/", userRouter)๋Š” ๋ฃจํŠธ ๊ฒฝ๋กœ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ์š”์ฒญ์„ userRouter๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค์ •ํ•œ๋‹ค.
  • ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ /channels๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ์š”์ฒญ์€ channelRouter๊ฐ€ ์ฒ˜๋ฆฌํ•œ๋‹ค.

โญ๋ผ์šฐํŒ…
์š”์ฒญ์ด ๋‚ ์•„์™”์„ ๋•Œ ํ•ด๋‹น ์š”์ฒญ์˜ URL๊ณผ HTTP ๋ฉ”์„œ๋“œ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์ฝ”๋“œ๋‚˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๊ณผ์ •


๐Ÿ”€๋ผ์šฐํ„ฐ ๋ชจ๋“ˆํ™”

Express์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” express.Router() ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๋ผ์šฐํ„ฐ๋ฅผ ๋ชจ๋“ˆํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค. ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด ํ–ฅ์ƒ๋˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค.

 

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

/
โ”œโ”€โ”€ app.js               // ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฉ”์ธ ํŒŒ์ผ
โ””โ”€โ”€ routes/
    โ”œโ”€โ”€ user-demo.js     // ์‚ฌ์šฉ์ž ๊ด€๋ จ ๋ผ์šฐํ„ฐ
    โ””โ”€โ”€ channel-demo.js  // ์ฑ„๋„ ๊ด€๋ จ ๋ผ์šฐํ„ฐ

์—ฌ๊ธฐ์„œ routes ํด๋”๋Š” ๋ชจ๋“  ๋ผ์šฐํ„ฐ ํŒŒ์ผ์„ ๊ด€๋ฆฌํ•˜๋Š” ๋””๋ ‰ํ† ๋ฆฌ์ด๋‹ค. ๊ฐ ๋ผ์šฐํ„ฐ ํŒŒ์ผ์€ ๊ด€๋ จ ๋ฆฌ์†Œ์Šค(์‚ฌ์šฉ์ž, ์ฑ„๋„)์— ๋Œ€ํ•œ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ •์˜ํ•œ๋‹ค.

 

app.js

์„œ๋ฒ„ ์„ค์ • ๋ฐ ๋ผ์šฐํ„ฐ ๋“ฑ๋ก

const express = require("express");
const app = express();

app.listen(7773);

const userRouter = require("./routes/user-demo");
const channelRouter = require("./routes/channel-demo");

app.use("/", userRouter);
app.use("/channels", channelRouter);

app.js๋Š” Express ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ง„์ž…์ ์œผ๋กœ, ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•˜๊ณ  ํ•„์š”ํ•œ ๋ผ์šฐํ„ฐ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. ํ˜„์žฌ, ๋‘ ๊ฐœ ๋ผ์šฐํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

  • userRouter: ๋ฃจํŠธ ๊ฒฝ๋กœ์— ๋“ฑ๋ก๋˜์–ด ์‚ฌ์šฉ์ž ๊ด€๋ จ API ์ฒ˜๋ฆฌ
  • channelRouter: /channels ๊ฒฝ๋กœ์— ๋“ฑ๋ก๋˜์–ด ์ฑ„๋„ ๊ด€๋ จ API๋ฅผ ์ฒ˜๋ฆฌ

 

์ฝ”๋“œ ๊ฐœ์„ ์‚ฌํ•ญ

1. app->router๋กœ ๋ณ€๊ฒฝ
๊ธฐ์กด ์ฝ”๋“œ์—์„œ๋Š” ๋ชจ๋“  ๋ผ์šฐํŠธ๋ฅผ app ๊ฐ์ฒด์— ์ง์ ‘ ๋“ฑ๋กํ–ˆ์ง€๋งŒ, ๋ฆฌํŒฉํ† ๋ง๋œ ์ฝ”๋“œ์—์„œ๋Š” Express Router๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“ˆํ™”ํ•˜์˜€๋‹ค.

//๋ณ€๊ฒฝ ์ „
app.post("/login", function (req, res) { /* ... */ });
app.post("/signup", function (req, res) { /* ... */ });

//๋ณ€๊ฒฝ ํ›„
const router = express.Router();

router.post("/login", function (req, res) { /* ... */ });
router.post("/signup", function (req, res) { /* ... */ });

module.exports = router;

 

2. userId๋ฅผ Key๋กœ ์‚ฌ์šฉ
๊ธฐ์กด์—๋Š” ์ˆœ์ฐจ์ ์ธ id๋ฅผ key๋กœ ์‚ฌ์šฉํ–‡์ง€๋งŒ, userId๋ฅผ key๋กœ ์‚ฌ์šฉํ•˜๋„๋ก ๋ณ€๊ฒฝํ•˜์˜€๋‹ค.

//๋ณ€๊ฒฝ ์ „
let db = new Map();
var id = 1;

app.post("/signup", function (req, res) {
  if (req.body !== undefined && req.body.userId) {
    db.set(id++, req.body);
    // ...
  }
});

//๋ณ€๊ฒฝ ํ›„
let db = new Map();
// var id = 1; // ๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์Œ - userId๋ฅผ ํ‚ค๋กœ ์‚ฌ์šฉ

router.post("/signup", function (req, res) {
  if (req.body !== undefined && req.body.userId) {
    const userId = req.body.userId;

    // ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์•„์ด๋””์ธ์ง€ ํ™•์ธ
    if (db.has(userId)) {
      return res.status(409).json({ message: "์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์•„์ด๋””์ž…๋‹ˆ๋‹ค." });
    }

    db.set(userId, req.body);
    // ...
  }
});
  • userId๋กœ ์‚ฌ์šฉ์ž๋ฅผ ๋ฐ”๋กœ ์ฐพ์„ ์ˆ˜ ์žˆ์–ด ์กฐํšŒ ์„ฑ๋Šฅ ํ–ฅ์ƒ
  • ์ค‘๋ณต ์•„์ด๋”” ํ™•์ธ์ด db.has(userId)๋กœ ๊ฐ„๋‹จํ•ด์ง
  • ๋ณ„๋„์˜ id ๋ณ€์ˆ˜ ๊ด€๋ฆฌ ๋ถˆํ•„์š”

 

3. ์˜ˆ์™ธ์ฒ˜๋ฆฌ ๊ฐœ์„ 
์ฝ”๋“œ ์ „๋ฐ˜์— ๊ฑธ์ณ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๋” ๊ฐ•ํ™”ํ•˜์˜€๋‹ค.

 

๋กœ๊ทธ์ธ ์‹œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ

router.post("/login", function (req, res) {
  // userId๋‚˜ password๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
  if (!req.body || !req.body.userId || !req.body.password) {
    return res
      .status(400)
      .json({ message: "์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”." });
  }

  // ...userId๋ฅผ ์ง์ ‘ ํ‚ค๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์กฐํšŒ
  const loginUser = db.get(userId);

  if (!loginUser) {
    return res.status(401).json({ message: "์กด์žฌํ•˜์ง€ ์•Š๋Š” ์•„์ด๋””์ž…๋‹ˆ๋‹ค" });
  }

  // ...
});

 

์ฑ„๋„ API์—์„œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ

// ์ฑ„๋„์„ ์ฐพ์ง€ ๋ชปํ–ˆ์„ ๋•Œ ๊ณตํ†ต ์‘๋‹ต ํ•จ์ˆ˜
function notFoundChannel(res) {
  return res.status(404).json({ message: "์ฑ„๋„์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค." });
}

// ์‚ฌ์šฉ ์˜ˆ
router.get("/user/:userId", (req, res) => {
  // ...

  if (db.size === 0) {
    return notFoundChannel(res);
  }

  // ...

  if (userChannels.length === 0) {
    return notFoundChannel(res);
  }

  // ...
});
  • notFoundChannel ๊ฐ™์€ ๊ณตํ†ต ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ค‘๋ณต ์ฝ”๋“œ๋ฅผ ๊ฐ์†Œ
  • return์„ ํ™œ์šฉํ•œ early exit ํŒจํ„ด์œผ๋กœ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ

 

์ฑ„๋„ API ์ถ”๊ฐ€ ๊ธฐ๋Šฅ

// ํŠน์ • ์‚ฌ์šฉ์ž๊ฐ€ ์ƒ์„ฑํ•œ ์ฑ„๋„ ๋ชฉ๋ก ์กฐํšŒ
router.get("/user/:userId", (req, res) => {
  const { userId } = req.params;

  if (db.size === 0) {
    return notFoundChannel(res);
  }

  const userChannels = [];

  db.forEach((channel, key) => {
    if (channel.userId === userId) {
      userChannels.push({ ...channel, id: key });
    }
  });

  if (userChannels.length === 0) {
    return notFoundChannel(res);
  }

  res.status(200).json({
    message: "์‚ฌ์šฉ์ž ์ฑ„๋„ ์กฐํšŒ ์„ฑ๊ณต",
    channels: userChannels,
  });
});

//์ฑ„๋„ ์ƒ์„ฑ ์‹œ userId ํ•„๋“œ
let channel = {
  channelName: req.body.channelName,
  userId: req.body.userId,  // ์‚ฌ์šฉ์ž ID ์ถ”๊ฐ€
  createdAt: new Date().toISOString(),
};   

์ฑ„๋„ ์ƒ์„ฑ์‹œ userId ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์–ด๋–ค ์‚ฌ์šฉ์ž๊ฐ€ ์ฑ„๋„์„ ์ƒ์„ฑํ–ˆ๋Š”์ง€ ์ถ”์ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด์ œ, ํŠน์ • ์‚ฌ์šฉ์ž๊ฐ€ ์ƒ์„ฑํ•œ ๋ชจ๋“  ์ฑ„๋„์„ ์กฐํšŒํ•˜๋Š” API๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.

 

โŒ์•ฝ๊ฐ„์˜ ๋ณด์•ˆ์ด์Šˆ

userId ๊ฐ™์€ ๊ฐ’์€ ์›๋ž˜ ํ—ค๋”์— ์‹ค์–ด๋ณด๋‚ด๋Š” ๊ฐ’์ธ๋ฐ, ์šฐ์„  ๋ฐ”๋””์— ํฌํ•จํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์‚ฌ์šฉ์ž ์ธ์ฆ์„ ์œ„ํ•ด JWT ๊ฐ™์€ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜๊ณ , ์ด ํ† ํฐ์€ ๋ณดํ†ต HTTP ํ—ค๋”์˜ Authorization ํ•„๋“œ์— ํฌํ•จ๋œ๋‹ค.

// ํ† ํฐ ์ธ์ฆ ๋ฐฉ์‹ ๋ง›๋ณด๊ธฐ
router.get("/users/me", authenticateToken, (req, res) => {
  // req.user๋Š” authenticateToken ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ†ตํ•ด ์„ค์ •๋œ ๊ฐ’
  const user = db.get(req.user.userId);
  res.json(user);
});

function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (token == null) return res.sendStatus(401);

  // ํ† ํฐ ๊ฒ€์ฆ ๋กœ์ง...
}

์ด๊ฒƒ๋„ ์ฐจ์ฐจ ๋ฐฐ์šธ ๋‚ด์šฉ...!


์ฝ”๋“œ ๊ฐœ์„  ์ž‘์—…์„ ํ†ตํ•ด Express ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์กฐํ™”์™€ ๋ชจ๋“ˆํ™”, ๊ทธ๋ฆฌ๊ณ  ๋” ๋‚˜์€ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์„ ์•Œ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ํ•˜๋‚˜ํ•˜๋‚˜ ๋ฐฐ์šธ์ˆ˜๋ก ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๋” ๊ตฌ์กฐ์ ์œผ๋กœ API๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์„์ง€ ์ƒ๊ฐ์ด ์ ์  ๋งŽ์•„์ง€๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

์ด์ œ๋Š” ์ง„์งœ ๊ธฐ๋Šฅ๋„ ๋‹ค์–‘ํ•œ๋ฐ, ์Šฌ์Šฌ ๋ฐ์ดํ„ฐ ์—ฐ๋™์„ ํ•ด๋ณด์ง€ ์•Š์œผ๋ ค๋‚˜. ์˜ค๋Š˜ ๊ฐ•์˜ ์ค‘ ERD ํŒŒํŠธ๋„ ์žˆ๋˜๋ฐ ์Šฌ์Šฌ DB ์—ฐ๋™๋„ ํ•˜์ง€ ์•Š์„๊นŒ ๊ธฐ๋Œ€์ค‘์ž…๋‹ˆ๋‹ค.