👩💻실습 순서
GET/hello 실습하기
GET/foods 실습하기
POST/foods 실습하기
내가 생각한 방식으로 GET, POST 구상하기 <<< 다음 사례글에서
내가 생각한 방식으로 GET, POST 구현하기
🛠️사용 도구
Cursor(모델은 주로 Grok 4.5)
💦진행 과정
먼저 API 서버를 갓 배우는 초보자의 눈높이에 맞춰 설명하면서 구현해달라고 했습니다.
엔드포인트의 개념
엔드포인트
역할
GET /hello
서버가 살아있는지 확인하는 인사
GET /나만의-무언가
저장된 데이터 조회
POST /나만의-무언가
새 데이터 보내기/저장
추천 언어 : Node.js (JavaScript/TypeScript) + Express
이유
- 웹/API 예제가 가장 많음
- AI 도구와 코드 예시가 풍부함
- 프론트(JS)랑 문법이 비슷해서 나중에 확장하기 쉬움
- 설치-실행이 비교적 단순함
시작 전에 사용자가 결정해야 할 것
1\. 언어/프레임워크
예: Node.js + Express 또는 Python + FastAPI
2\. 나만의 엔드포인트 주제
예: /memo, /todo, /mood, /quote
→ 경로 이름과 "무엇을 주고받는지"를 정해야 함
3\. 데이터 형태 (JSON)
예:
```json
{ "id": 1, "text": "오늘 공부하기", "done": false }
```
4\. 저장 방식
초보 실습이면 메모리(배열)로 충분
(서버 재시작하면 데이터 사라짐 --- 정상)
5\. 포트
보통 3000 또는 8000
6\. 테스트 방법
브라우저(GET만), 또는 Postman / Thunder Client / curl
포트 3000과 8000의 차이가 궁금해서 물어봤습니다.
포트 3000 vs 8000
기능 차이는 없습니다. 둘 다 "서버가 문 열고 기다리는 번호"일 뿐이에요.
3000
8000
의미
그냥 번호
그냥 번호
주요 사용처
Node/React 예제에서 자주 씀
Python(Django/FastAPI) 예제에서 자주 씀
선택기준
비어 있으면 아무거나 OK
비어 있으면 아무거나 OK
주의할 점만 있으면 됩니다.
- 이미 다른 프로그램이 그 포트를 쓰면 충돌남
- URL은 http://localhost:선택한포트 형태
그리고 터미널에 pnpm start로 실행하고 3000으로 들어가면 확인해볼 수 있다고 해서 주소창에 http://localhost:3000이라고 입력했습니다.
**Cannot GET /**이라고 나오더라고요.
당황해서 왜 그런지 물어봤어요.
원인은 단순해요. `http://localhost:3000/`에는 원래 엔드포인트가 없었어요.
서버는 살아 있는데, / 경로만 정의가 안 되어 있어서 Cannot GET /이 뜬 겁니다.
😮깨달은 것 1
아,, url에 지정한 경로도 같이 넣어줘야 실행이 되는구나....
예전에 바이브코딩 할 때 로컬로 테스트를 하면 localhost:8000 같이 포트까지만 입력하면 접속이 잘됐어서 뒷통수가 얼얼한 경험이었습니다. 생각해보면 웹사이트 메인 주소에서 /다음으로 이런저런 경로가 뜨잖아요? 그게 다 그냥 갖다 붙인 게 아니라는 걸 느꼈습니다.
그렇게 hello와 foods에 대해 조회한 후 POST로 넘어가기 위해 POST는 어떻게 해야하냐고 물어봤습니다.
브라우저 주소창으로는 POST를 할 수 없어요. GET만 됩니다.
POST는 “데이터를 보내면서” 요청해야 해서, 아래 중 하나로 테스트하면 됩니다.
## 1) 가장 쉬운 방법: Thunder Client / Postman
VS Code/Cursor 확장 Thunder Client 또는 앱 Postman을 쓰면 됩니다.
1. Method를 POST로 선택
2. URL: http://localhost:3000/foods
3. Body → JSON 선택
4. 아래처럼 입력:
json
{
"name": "초밥",
"note": "주말에"
}5. Send 클릭
그다음 브라우저에서 http://localhost:3000/foods를 열면 목록에 추가된 걸 볼 수 있어요.
## 2) 터미널로 하는 방법 (PowerShell)
서버가 켜진 상태에서 새 터미널을 열고:
powershell
Invoke-RestMethod -Method POST -Uri http://localhost:3000/foods -ContentType "application/json" -Body '{"name":"초밥","note":"주말에"}'터미널로 하는 방법을 선택하고 하라는대로 입력했습니다.
텍스트 상자가 있는 웹페이지의 스크린샷
'엇 왜 ?가 나오지? 조회하면 정상적으로 뜨려나??' <<< ?? 로 조회됨
ai의 답변은 간단했습니다.
PowerShell에서 -Body '{"name":"초밥"...}'처럼 바로 넣으면, 한글이 **깨진 채(또는 ?로)** 서버에 전달됩니다.
서버는 받은 그대로 저장해서 ???가 된 거예요.
## 해결: UTF-8로 보내기
이 명령어로 다시 해보세요.
powershell
$body = @{ name = "초밥"; note = "주말에" } | ConvertTo-Json
Invoke-RestMethod -Method POST -Uri http://localhost:3000/foods -ContentType "application/json; charset=utf-8" -Body ([System.Text.Encoding]::UTF8.GetBytes($body))???는 그대로 남아 있을 수 있어요.
서버를 한 번 재시작하면 메모리 데이터가 초기화되고, 위 명령으로 다시 추가하면 한글로 정상 저장됩니다.
Thunder Client/Postman을 쓰면 이런 인코딩 문제를 거의 안 겪습니다.
😮깨달은 것 2 : 아 터미널에 한글은 막 입력하지 말고 한번 코딩을 해줘야 하는구나. 난 UTF-8없이는 살 수 없겠는걸.
다음으로 바이브코딩으로 생성된 index.js의 코드를 차근차근 설명해달라고 부탁했습니다. 내용이 길어서 제가 새로 안 것들 위주로 뽑아보았습니다.
const express = require("express");
const app = express();
const PORT = process.env.PORT || 3000;require("express"): Express 라이브러리를 가져옵니다.app: 실제 서버 앱입니다. “이 주소로 오면 이렇게 응답해”를 여기에 등록합니다.서버는 포트로 접속을 받습니다. (문 번호라고 생각하면 됩니다.)
process.env.PORT가 있으면 그걸 쓰고, 없으면3000을 씁니다.배포 환경(예: Render)에서는 보통
PORT를 환경변수로 줍니다.
// 먹고 싶은 음식을 메모리에 저장 (서버 재시작하면 초기화됨)
const foods = [
{ id: 1, name: "김치찌개", note: "매콤하게" },
{ id: 2, name: "돈까스", note: "저녁에" },
];
let nextId = 3;foods: 음식 목록 배열. DB가 아니라 서버 메모리에만 있습니다.서버를 끄거나 재시작하면 처음 상태로 돌아갑니다.
nextId: 다음에 붙일id번호입니다. (지금은 1, 2가 있으니 다음은 3)
// GET / — 사용 가능한 주소 안내
app.get("/", (req, res) => {
res.json({
message: "API 서버가 실행 중입니다.",
endpoints: [
{ method: "GET", path: "/hello" },
{ method: "GET", path: "/foods" },
{ method: "POST", path: "/foods" },
],
});
});
// 1) GET /hello — 서버 인사
app.get("/hello", (req, res) => {
res.json({ message: "hello" });
});
// 3) POST /foods — 먹고 싶은 음식 추가
// body 예시: { "name": "초밥", "note": "주말에" }
app.post("/foods", (req, res) => {
const { name, note } = req.body;
if (!name || typeof name !== "string") {
return res.status(400).json({
error: "name은 필수이고 문자열이어야 합니다.",
});
}
const food = {
id: nextId++,
name: name.trim(),
note: typeof note === "string" ? note.trim() : "",
};
foods.push(food);
res.status(201).json({ food });
});라우트 : app.get("/주소", 함수) / app.post("/주소", 함수) 형태입니다.
GET: 조회 (데이터 가져오기)
POST: 생성 (새 데이터 보내기)
req: 요청 정보res: 응답을 보내는 객체
POST의 흐름:
req.body에서name,note를 꺼냅니다.name이 없거나 문자열이 아니면 400 에러를 보냅니다.새 음식 객체를 만듭니다.
id:nextId를 쓰고 1 증가 (nextId++)name: 앞뒤 공백 제거note: 문자열이면 trim, 아니면 빈 문자열
foods배열에 넣고201(Created)과 함께 방금 만든 음식을 돌려줍니다.
😮깨달은 것 3 : 아 req=request(요청), res=response(응답)!!!
함수가 뭔가 요청>>>응답의 반복으로 되어있는 것 같네.
여기까지 하나의 서버로 세가지 엔드포인트를 구현하는 실습을 마쳤습니다.
다른 분들은 예쁜 ui 적용해서 마법을 부리시는데 저는 보시다시피 기본을 따라가는 것도 꽤나 일이네요;;
다음에는 공공데이터포털의 공개 데이터를 이용해서 도로명주소를 입력하면 주변의 상가를 조회(GET)하고 북마크, 메모를 남기는(POST) 것을 실습해보려고 합니다.