본문 바로가기
📌 Front End/└ Node.js

[Node.js] Express API 서버 구축 - RESTful 설계, 라우팅, 다중 핸들러 및 응답 처리

by 쫄리_ 2024. 11. 18.
728x90
반응형

📌 Express API

클라이언트의 요청 경로(URL)에 따라 작업을 처리하고 응답을 반환하는 Node.js 프레임워크
간단한 코드로 요청 경로와 핸들러를 연결하여 API 서버 구축에 적합합니다.

 


🗂️ Express API 서버 구축

 터미널 열기 : Ctrl + Shift + `
 터미널 프로세스 종료 : Ctrl + C

✅ 프로젝트 초기화

새로운 Node.js 프로젝트 생성

-y 옵션은 기본 설정으로 '📜package.json' 파일' 을 생성합니다.

npm init -y

 

'📜package.json' 파일 스크립트 추가
  "scripts": {
    "start": "node server.js",
  },

✅ Express 설치

ExpressAPI 서버를 간단히 구축할 수 있는 Node.js 프레임워크입니다.

npm i express

⚙️ server.js 서버 파일 생성

Express 애플리케이션을 초기화하고 서버를 실행합니다.

  • 기본 라우팅 : 단일 변수를 사용해 요청 처리
  • 중첩 라우팅 : 여러 동적 변수를 사용해 요청 처리
  • req.params : URL 경로에서 동적 변수 값을 추출하는 객체
  • 포트 설정 : 서버는 9999번 포트에서 실행
// ▶ ⚙️server.js
//    🌐http://localhost:9999
const express = require('express');
const app = express();

app.listen(9999);

🔎 기본 라우팅 (Path Parameter)

기본 라우팅단일 경로 변수(:id)를 사용하여 요청 처리

  • GET 요청 : http://localhost:9999/board/30
  • 서버 응답 : { "boardID": "30" }
// ▶ ⚙️server.js
const express = require('express');
const app = express();

app.get('/board/:id', (req, res) => {
    const { id } = req.params; // 경로에서 'id' 변수 추출
    res.status(200).send({
        boardID: id  // 응답 데이터에 'id' 포함
    });
});

app.listen(9999);

🔎 중첩 라우팅

중첩 라우팅여러 동적 변수(:id, :pw, :name)를 사용하여 요청 처리

  • GET 요청 : http://localhost:9999/board/thgus/1234/권소현
  • 서버 응답 : { "boardID":"thgus", "pw":"1234", "name":"권소현" }
// ▶ ⚙️server.js
const express = require('express');
const app = express();

app.get('/board/:id/:pw/:name', (req, res) => {
    const { id, pw, name } = req.params; // 경로에서 'id', 'pw', 'name' 변수 추출
    res.status(200).send({
        boardID: id, // 'id' 값 포함
        pw,          // 'pw' 값 포함
        name         // 'name' 값 포함
    });
});

app.listen(9999);

➕ Path Parameter  VS  Query String  비교

Express에서는 데이터를 URL 경로로 전달하는 두 가지 주요 방식이 있습니다.

방식 설명 예시
Path Parameter 경로 일부를 변수처럼 사용
➡️ 특정 리소스를 식별할 때 사용
/users/:id ➡️ /users/123
Query String URL 뒤에 키-값 쌍으로 전달
➡️ 필터링/정렬 같은 부가 정보를 전달할 때 사용
/search?keyword=express
// ➡️ Path Parameter
/* - 요청 : GET /users/1
   - 응답 : User ID: 1
*/
app.get('/users/:id', (req, res) => {
  const userId = req.params.id; // Path Parameter에서 ID 추출
  res.send(`User ID: ${userId}`);
});

// ➡️ Query String
/* - 요청 : GET /search?keyword=express
   - 응답 : 검색 키워드: express
*/
app.get('/search', (req, res) => {
  const keyword = req.query.keyword; // Query String에서 키워드 추출
  res.send(`검색 키워드: ${keyword}`);
});

사용예시

// ▶ ⚙️server.js
//    🌐http://localhost:9999

// 1️⃣Express 모듈 로드
const express = require('express');

// 2️⃣Express 애플리케이션 객체 생성
const app = express();

// 3️⃣기본 라우팅
app.get('/board/:id', (req, res) => {
    const { id } = req.params;
    res.status(200).send({ boardID: id });
});

// 4️⃣중첩 라우팅
app.get('/board/:id/:pw/:name', (req, res) => {
    const { id, pw, name } = req.params;
    res.status(200).send({
        boardID: id,
        pw,
        name,
    });
});

// 5️⃣서버 실행
app.listen(9999, () => {
    console.log('Server is running on http://localhost:9999');
});

 


 서버 실행

🌐 http://localhost:9999

npm start

 


📌 Express 라우트 구조

Express에서 라우트클라이언트의 요청을 받고 처리한 후 응답을 보내는 역할

  • app : Express 애플리케이션 인스턴스
  • METHOD : HTTP 요청 메서드(GET, POST, PUT, DELETE 등)
  • PATH : 클라이언트 요청할 경로
  • HANDLER : 요청을 처리하고 응답을 반환하는 함수(콜백)
app.METHOD(PATH, HANDLER);

 

 

사용예시

  • app.get : 클라이언트의 GET 요청 처리
  • '/' : 루트 경로(기본 홈페이지)
  • res.send : 응답으로 메시지 보내기("Hello World!" 응답 반환)
app.get('/', (req, res) => {
    res.send('Hello World!');
});

 


📌 HTTP 메서드  VS  RESTful API

Express에서는 HTTP 메서드를 사용하여 클라이언트 요청을 처리합니다.

RESTful API 설계클라이언트-서버 간의 데이터 통신을 효율적으로 처리할 수 있도록 설계된 표준

 

🔎 HTTP 메서드와 역할

메서드 설명 예시
GET 데이터 조회 GET /users
POST 새로운 데이터 생성 POST /users
PUT 기존 데이터 전체 수정 PUT /users/:id
PATCH 기존 데이터 부분 수정 PATCH /users/:id
DELETE 데이터 삭제 DELETE /users/:id

 

🔎 RESTful API 설계 원칙

  1. 리소스 기반 경로
    - 데이터를 명사 형태 표현
       예 : /users, /products
  2. HTTP 메서드 동작 정의
    - 생성(POST) / 조회(GET) / 수정(PUT) / 삭제(DELETE) 사용
  3. 계층적 경로 설계 
    - 데이터 관계를 계층적으로 표현
      예 : /users/1/orders (ID 1 사용자의 주문 목록)
// ➡️ 전체 조회
app.get('/users', (req, res) => res.json([])); 

// ➡️ 새 사용자 생성
app.post('/users', (req, res) => res.send('새 사용자 생성'));

// ➡️ 사용자 수정
app.put('/users/:id', (req, res) => res.send(`User ${req.params.id} 수정`));

// ➡️ 사용자 삭제
app.delete('/users/:id', (req, res) => res.send(`User ${req.params.id} 삭제`));

 


📌 Express 라우트 경로 설정

Express에서는 라우트 경로를 정의하여 클라이언트 요청을 처리합니다.

라우트 경로문자열, 패턴, 또는 정규식으로 정의할 수 있습니다.

  • 문자열 경로 : 요청 경로정확히 일치
  • 문자열 패턴 : 특정 문자 패턴 일치
  • 정규식 경로 : 복잡한 경로 패턴 처리

🔎 문자열 경로

가장 간단한 형태로, 경로를 문자열로 정의합니다.
요청 경로가 정확히 일치해야 핸들러가 실행됩니다.

  • GET / ➡️ 화면
  • GET /about ➡️ 소개 페이지
  • GET /about/customer ➡️ 고객 소개 페이지
// '/', '/about', '/about/customer'
app.get('/', (req, res) => res.send('홈 화면'));
app.get('/about', (req, res) => res.send('소개 페이지'));
app.get('/about/customer', (req, res) => res.send('고객 소개 페이지'));

🔎 문자열 패턴 경로

특정 패턴을 정의하여 유연하게 경로를 처리할 수 있습니다.

  • GET /acd ➡️ b0개 또는 1개 포함
  • GET /abcd ➡️ b1개 이상 포함
  • GET /ab123cd ➡️ ab와 cd 사이문자 포함
  • GET /abe ➡️ cd 0개 또는 1개 포함
패턴 설명 예시
/ab?cd 문자 b0개 또는 1개 포함
➡️ ? 해당문자 0개 또는 1개 포함
/acd
/abcd
/ab+cd 문자 b1개 이상 포함
➡️ + 해당문자 1개 이상 포함
/abcd
/abbcd
/ab*cd ab와 cd 사이문자 0개 이상 포함
➡️ * 해당문자 0개 이상 포함
/abcd
/ab123cd
/abXYZcd
/ab(cd)?e 문자 cd0개 또는 1개 포함
➡️ ()? 그룹이 0개 또는 1개 포함
/abe
/abcde
app.get('/ab?cd', (req, res) => res.send('b가 0개 또는 1개 포함'));
app.get('/ab+cd', (req, res) => res.send('b가 1개 이상 포함'));
app.get('/ab*cd', (req, res) => res.send('ab와 cd 사이에 문자 포함'));
app.get('/ab(cd)?e', (req, res) => res.send('cd가 0개 또는 1개 포함'));

🔎 정규식 경로

정규 표현식(Regex) 사용하여 복잡한 경로를 정의
정규식은 패턴 일치를 확인하며, 경로가 일치하면 핸들러가 실행됩니다.

  • GET /abc ➡️ 경로a가 포함
  • GET /123a456 ➡️ 경로a가 포함
  • GET /insert123 ➡️ insert로 시작하는 경로
정규식 설명 예시
/a/ 경로문자 a가 포함된 경우 /abc
/123a456
/^insert/ 경로 insert로 시작하는 경우 /insert
/insert123
app.get(/a/, (req, res) => res.send('경로에 a가 포함됨'));
app.get(/^insert/, (req, res) => res.send('insert로 시작하는 경로'));

사용예시

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

// ▶ 문자열 경로
app.get('/', (req, res) => res.send('홈 화면'));
app.get('/about', (req, res) => res.send('소개 페이지'));
app.get('/about/customer', (req, res) => res.send('고객 소개 페이지'));

// ▶ 문자열 패턴 경로
app.get('/ab?cd', (req, res) => res.send('b가 0개 또는 1개'));
app.get('/ab+cd', (req, res) => res.send('b가 1개 이상'));
app.get('/ab*cd', (req, res) => res.send('ab와 cd 사이에 문자 포함'));
app.get('/ab(cd)?e', (req, res) => res.send('cd가 0개 또는 1개'));

// ▶ 정규식 경로
app.get(/a/, (req, res) => res.send('경로에 a가 포함됨'));
app.get(/^insert/, (req, res) => res.send('insert로 시작하는 경로'));

// 서버 실행
app.listen(3000, () => console.log('서버 실행 중: http://localhost:3000'));

 


📌 Express 다중 핸들러

Express에서 다중 핸들러 하나의 요청에 대해 여러 작업을 단계적으로 처리

각 핸들러는 요청(req)응답(res) 객체를 다루며, next()를 호출해 다음 핸들러로 흐름 전달
핸들러는 3개의 주요 매개변수를 사용합니다.

  • req : 클라이언트 요청(Request) 객체
  • res : 서버 응답(Response) 객체
  • next : 다음(Next) 미들웨어 또는 핸들러로 넘어가게 하는 함수

🔎 하나의 요청에 여러 핸들러 사용

next()를 사용하여 요청 처리 흐름을 다음 핸들러로 넘길 수 있습니다.

  • 첫 번째 핸들러 : 실행 후 console.log()로 메시지 출력
  • next() 호출 : 두 번째 핸들러로 요청 흐름 전달
  • 두 번째 핸들러 : res.send()를 사용해 응답을 전송하며 요청 처리 완료
app.get('/example', (req, res, next) => {
    console.log('첫 번째 핸들러 실행');
    // ▶ 다음 핸들러 호출
    next(); 
}, (req, res) => {
    res.send('두 번째 핸들러 실행'); // 클라이언트에게 응답 전송
});

🔎 콜백 함수 배열 사용

핸들러를 배열로 정의하여 요청 처리를 단계적으로 나눌 수 있습니다.

이를 통해 코드 재사용성과 가독성을 높일 수 있습니다.

  • handler1 실행 ➡️ 메시지 출력 후 next()handler2 호출
  • handler2 실행 ➡️ 메시지 출력 후 next()handler3 호출
  • handler3 실행 ➡️ 클라이언트에게 응답 전송(res.send())
const handler1 = (req, res, next) => {
    console.log('첫 번째 핸들러');
    next(); // 다음 핸들러 호출
};

const handler2 = (req, res, next) => {
    console.log('두 번째 핸들러');
    next(); // 다음 핸들러 호출
};

const handler3 = (req, res) => {
    res.send('세 번째 핸들러에서 응답 전송');
};

// ▶ 핸들러 배열로 정의
app.get('/example', [handler1, handler2, handler3]);

사용예시

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

// ▶ 핸들러 정의
const handler1 = (req, res, next) => {
    console.log('첫 번째 핸들러 실행');
    next();
};

const handler2 = (req, res, next) => {
    console.log('두 번째 핸들러 실행');
    next();
};

const handler3 = (req, res) => {
    res.send('세 번째 핸들러에서 응답 전송');
};

// ▶ '/example' 경로에서 다중 핸들러 실행
app.get('/example', [handler1, handler2, handler3]);

// 서버 실행
app.listen(3000, () => console.log('서버 실행 중: http://localhost:3000'));

 


📌 Express 응답 메서드

Express에서 응답 메서드서버가 클라이언트 요청에 응답할 때 사용
클라이언트에게 데이터를 전송하거나 작업 완료 상태를 알릴 수 있습니다.

응답 메서드
설명
예시
res.download()
클라이언트에
파일 다운로드 프롬프트 전송
res.download(
   '/path/to/file'
)
res.end()
응답 프로세스 종료
res.end()
res.json()
JSON 데이터응답으로 전송
res.json(
   { message: 'Hello, JSON!' }
)
res.jsonp()
JSONP 형식으로
JSON 데이터를 응답
res.jsonp( 
   { message: 'Hello, JSONP!' }
)
res.redirect()
클라이언트를
지정된 경로로 리다이렉트
res.redirect(
   '/new-path'
)
res.render()
뷰 템플릿을 렌더링하여
HTML로 응답
res.render( 
   'index', { title: 'My Page' }
)
res.send()
문자열, 객체, HTML
다양한 데이터를 응답
res.send(
   '<h1>Hello, World!</h1>'
)
res.sendFile()
지정된 파일 데이터
클라이언트에 전송
res.sendFile(
   '/path/to/image.jpg'
)
res.sendStatus()
HTTP 상태 코드를 설정하고
메시지를 응답
res.sendStatus( 
   404
)

 

 

사용예시

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

// ▶ JSON 데이터 응답
app.get('/json', (req, res) => {
    res.json({ message: 'Hello, JSON!' }); // JSON 데이터 전송
});

// ▶ 파일 다운로드
app.get('/download', (req, res) => {
    res.download('./files/example.txt'); // example.txt 파일 다운로드
});

// ▶ 리다이렉트
app.get('/old-path', (req, res) => {
    res.redirect('/new-path'); // 클라이언트를 /new-path로 이동
});

// ▶ 텍스트 응답
app.get('/text', (req, res) => {
    res.send('Hello, World!'); // 텍스트 응답
});

// ▶ 상태 코드 응답
app.get('/not-found', (req, res) => {
    res.sendStatus(404); // 404 Not Found 상태 코드 응답
});

// 서버 실행
app.listen(3000, () => {
    console.log('서버 실행 중: http://localhost:3000');
});

 


📌 Express 라우트 관리

Express에서는 라우트 처리를 통해 클라이언트 요청을 처리하고 응답을 보냅니다.
app.route()express.Router()를 활용하면 효율적이고 체계적인 라우트 관리가 가능합니다.


🔎 라우트 관리 방식

app.route()

  • 한 경로에서 여러 HTTP 메서드 처리
  • 간단한 라우트에 적합

express.Router()

  • 라우트를 파일로 분리해 모듈화
  • 큰 프로젝트복잡한 라우트 구조에 유용
  • 코드 가독성과 유지보수성을 크게 향상
특징 app.route() express.Router()
사용 목적 한 경로에서 여러 HTTP 메서드 처리 라우트를 파일로 분리하고 모듈화
적합한 상황 간단한 라우트 처리 대규모 프로젝트나 복잡한 구조
코드 재사용성 낮음 높음
코드 가독성 간단한 라우트에서 높음 모듈화된 구조에서 높음

🔎 app.route()

하나의 경로에서 여러 HTTP 메서드 처리(GET, POST, PUT, DELETE 등)

app.route('/customer')
  .get((req, res) => res.send('고객 정보 조회'))     // GET 요청 처리
  .post((req, res) => res.send('신규 고객 추가'))    // POST 요청 처리
  .put((req, res) => res.send('고객 정보 수정'))     // PUT 요청 처리
  .delete((req, res) => res.send('고객 정보 삭제')); // DELETE 요청 처리

🔎 express.Router()

라우트를 여러 파일로 분리하여 관리
이 방식은 라우트가 많아질 때 코드 가독성유지보수성을 크게 향상시킵니다.

 

사용예시

 

📜 routes/customer.js

// ▶ 📜routes/customer.js
const express = require("express");
const router = express.Router();

router
  .get("/", (req, res) => res.send("고객 정보 조회"))           // GET 요청
  .post("/insert", (req, res) => res.send("신규 고객 추가"))    // POST 요청
  .put("/update", (req, res) => res.send("고객 정보 수정"))     // PUT 요청
  .delete("/delete", (req, res) => res.send("고객 정보 삭제")); // DELETE 요청

module.exports = router;

 

 

📜 routes/product.js

// ▶ 📜routes/product.js
const express = require("express");
const router = express.Router();

router
  .get("/", (req, res) => res.send("상품 정보 조회"))           // GET 요청
  .post("/insert", (req, res) => res.send("신규 상품 추가"))    // POST 요청
  .put("/update", (req, res) => res.send("상품 정보 수정"))     // PUT 요청
  .delete("/delete", (req, res) => res.send("상품 정보 삭제")); // DELETE 요청

module.exports = router;

 

 

📜 app.js

express.Router로 분리한 라우트app.js에서 불러와 사용합니다.

// ▶ 📜app.js
const express = require('express');
const customerRoute = require('./routes/customer'); // customer 라우트 추가
const productRoute = require('./routes/product');   // product 라우트 추가
const app = express();

// 1️⃣JSON 요청 파싱
app.use(express.json({ limit: '50mb' })); // 요청 본문 최대 50MB

// 2️⃣라우트 연결
app.use('/customer', customerRoute); // /customer 경로에 customer 라우트 연결
app.use('/product', productRoute); // /product 경로에 product 라우트 연결

// 3️⃣서버 실행
app.listen(3000, () => {
    console.log('Server started on http://localhost:3000');
});

 


 

 

728x90
반응형