오늘은 1월 5일 월요일입니다. 오늘은 역대 최고로 졸린 하루 였습니다. 그리고 조금 배운 개념도 있었지만 목 금 토 일을 쉬고 수업을 들을려고 하니 매우 힘드네요...
오늘은 웹 기초에 대해 배웠는데요 전공 수업 중 지능웹설계에 대해서 배운적이 있었는데 오늘은 그 개념과는 조금 다른 내용을 배웠습니다. 일단 이 블로그 방향에 대해서 어떻게 할지 고민이긴 한데 차차 나중에 더 좋은 블로그가 되어있지 않을까하는 생각이 있습니다. 누가 볼지는 모르겠지만... 그래도 나중에 제가 도움이 되는 블로그가 되도록 하겠슴돠
현대에서도 변호사의 의뢰인, 은행의 고객을 Client라고 부릅니다.
컴퓨터 세계에서 클라이언트는 서비스를 요청하는 쪽입니다.
컴퓨터 세계에서 서버는 서비스를 제공하는 쪽입니다.
식당의 직원(Server)이 손님에게 음식을 제공하는것처럼 서버는 클라이언트의 요청에 응답하여 데이터나 서비스를 제공합니다.
서버도 클라이언트 가능
클라이언트와 서버가 주고받는 메시지(패킷)는 편지와 비슷한 구조를 가집니다.
| 구분 | 요청 (Request) | 응답 (Response) |
|---|---|---|
| 비유 | "주문서" | "음식과 영수증" |
| 헤더 (Header) | 누구에게(Host), 어떤 언어로(Accept), 무엇을(Method) 원하는지 적혀있음 | 상태는 어떤지(Status Code), 어떤 형태의 데이터인지(Content-Type) 적혀있음 |
| 바디 (Body) | 추가 정보 (로그인 시 아이디/비번 등) - 없을 수도 있음 | 요청한 실제 데이터 (HTML, JSON, 이미지 등) |
인터넷 주소(도메인)는 거꾸로 된 나무(Tree)처럼 체계적인 순서가 있습니다.
주소 http://www.example.com 을 예로 들어볼게요. 우리는 왼쪽부터 읽지만, 컴퓨터는 오른쪽(뒤)부터 해석한답니다.
.kr(한국), .com(기업), .org(기관) 처럼 소속이나 국가를 나타내는 큰 분류입니다.| 종류 | 이름 | 설명 (쉽게 풀이) |
|---|---|---|
| A | 주소 (Address) | 가장 기본! 도메인 이름에 해당하는 IP 주소(IPv4)를 알려줍니다. |
| AAAA | 주소 (IPv6) | A 레코드와 같지만, 훨씬 긴 새로운 형식의 주소(IPv6)를 알려줍니다. |
| CNAME | 별명 (Canonical Name) | 도메인의 별명을 지어줍니다. |
| MX | 메일 (Mail Exchanger) | 이메일을 어디로 보내야 할지 알려줍니다. |
| TXT | 텍스트 (Text) | 메모장 같은 곳입니다. 주로 "이 도메인은 내 거야!"라고 인증하거나, 스팸 메일을 막는 용도로 쓰입니다. |
| NS | 네임 서버 (Name Server) | "이 도메인의 진짜 관리자(전화번호부 담당자)는 이 사람입니다"라고 알려주는 정보입니다. |
HTTP는 한 번 요청-응답이 끝나면 연결을 끊습니다.
| 특징 | 설명 | 비유 |
|---|---|---|
| 연결 유지 X | 응답 후 연결 종료 | 택배 기사가 물건 전달 후 떠남 |
| 장점 | 서버 자원 절약 | 한 기사가 여러 집 배달 가능 |
| 단점 | 매번 새로 연결 | 같은 집이어도 다시 찾아가야 함 |
HTTP는 이전 요청을 기억하지 않습니다.
sequenceDiagram
participant C as 🖥️ 클라이언트 (hgd)
participant S as 🖧 서버
C->>S: 첫 번째 요청: "로그인해줘"
S->>C: "로그인 완료!"
Note over S: 🧠 기억 없음
C->>S: 두 번째 요청: "내 정보 보여줘"
S->>C: "누구세요? 🤔"
클라이언트가 서버에 보내는 메시지입니다.
┌─────────────────────────────────────────────────┐
│ [1] 시작 라인 (Start Line) │
│ GET /users/hgd HTTP/1.1 │
├─────────────────────────────────────────────────┤
│ [2] 헤더 (Headers) │
│ Host: http://api.example.com │
│ Content-Type: application/json │
│ Authorization: Bearer token123 │
├─────────────────────────────────────────────────┤
│ [3] 빈 줄 (Empty Line) │
│ │
├─────────────────────────────────────────────────┤
│ [4] 본문 (Body) - 선택사항 │
│ { "name": "홍길동", "role": "instructor" }│
└─────────────────────────────────────────────────┘
GET /users/hgd HTTP/1.1
│ │ │
│ │ └─ HTTP 버전
│ └─ 요청 대상 경로 (URI)
└─ HTTP 메서드 (무엇을 할 것인가)
/* > : 요청이 날라가는거
/* < : 요청이 들어온거
| 메서드 | 의미 | 식당 비유 | 설명 |
|---|---|---|---|
| GET | 조회 | 메뉴판 보기 | 데이터를 가져옵니다 (읽기 전용) |
| POST | 생성 | 새 주문하기 | 새로운 데이터를 서버에 전송합니다 |
| PUT | 전체 수정 | 주문 전체 변경 | 기존 데이터를 완전히 교체합니다 |
| PATCH | 부분 수정 | 주문 일부 변경 | 데이터의 일부만 수정합니다 |
| DELETE | 삭제 | 주문 취소 | 데이터를 삭제합니다 |
GET vs POST 핵심 차이
GET /search?keyword=노션# hgd 사용자가 회원가입을 요청하는 경우
# POST 메서드로 /users 경로에 새 사용자 데이터를 전송
POST /users HTTP/1.1 # 시작 라인: 새 사용자 생성 요청
Host: http://api.cloudschool.com # 서버 주소
Content-Type: application/json # 본문 형식이 JSON임을 명시
Content-Length: 62 # 본문 길이 (바이트)
{ # 본문 (Body)
"username": "hgd", # 사용자명
"name": "홍길동", # 실명
"email": "mailto:abc@example.com" # 이메일
}
서버가 클라이언트에게 보내는 메시지입니다.
┌─────────────────────────────────────────────────┐
│ [1] 상태 라인 (Status Line) │
│ HTTP/1.1 200 OK │
├─────────────────────────────────────────────────┤
│ [2] 헤더 (Headers) |
│ Content-Type: application/json │
│ Content-Length: 85 |
├─────────────────────────────────────────────────┤
│ [3] 빈 줄 (Empty Line) │
│ │
├─────────────────────────────────────────────────┤
│ [4] 본문 (Body) │
│ { "id": 1, "name": "홍길동", ... } │
└─────────────────────────────────────────────────┘
HTTP/1.1 200 OK
│ │ │
│ │ └─ 상태 메시지 (사람이 읽기 쉬운 설명)
│ └─ 상태 코드 (기계가 이해하는 숫자)
└─ HTTP 버전
상태 코드는 세 자리 숫자이며, 첫 번째 숫자가 응답의 종류를 나타냅니다.
| 분류 | 범위 | 의미 | 비유 |
|---|---|---|---|
| 1xx | 100-199 | 정보 응답 | "알겠어, 처리 중이야" |
| 2xx | 200-299 | 성공 ✅ | "요청 잘 처리했어!" |
| 3xx | 300-399 | 리다이렉션 | "다른 곳으로 가봐" |
| 4xx | 400-499 | 클라이언트 오류 ❌ | "네가 잘못했어" |
| 5xx | 500-599 | 서버 오류 ❌ | "내가 잘못했어" |
| 코드 | 이름 | 의미 | 예시 상황 |
|---|---|---|---|
| 200 | OK | 요청 성공 | 정상적으로 데이터 조회 |
| 201 | Created | 생성 성공 | 새 회원가입 완료 |
| 204 | No Content | 성공했지만 반환할 데이터 없음 | 삭제 완료 |
| 400 | Bad Request | 잘못된 요청 | 필수 값 누락, 형식 오류 |
| 401 | Unauthorized | 인증 필요 | 로그인 안 함 |
| 403 | Forbidden | 권한 없음 | 접근 권한 부족 |
| 404 | Not Found | 찾을 수 없음 | 존재하지 않는 페이지 |
| 500 | Internal Server Error | 서버 내부 오류 | 서버 버그, 장애 |
# 회원가입 요청에 대한 성공 응답
# 201 Created: 새 리소스가 생성되었음을 의미
HTTP/1.1 201 Created # 상태 라인: 생성 성공
Content-Type: application/json # 응답 본문이 JSON 형식
Location: /users/1 # 생성된 리소스의 위치
Content-Length: 94 # 본문 길이
{ # 본문 (Body)
"id": 1, # 서버가 부여한 고유 ID
"username": "abc", # 요청한 사용자명
"name": "홍길동", # 요청한 실명
"createdAt": "2026-01-04T12:00:00Z" # 생성 시각
}
| 헤더 | 의미 | 예시 |
|---|---|---|
| Host | 요청 대상 서버 주소 | Host: [www.notion.so](http://www.notion.so) |
| Content-Type | 본문의 데이터 형식 | Content-Type: application/json |
| Content-Length | 본문의 길이 (바이트) | Content-Length: 348 |
| Authorization | 인증 정보 | Authorization: Bearer abc123 |
| User-Agent | 클라이언트 정보 | User-Agent: Chrome/120.0 |
| Accept | 클라이언트가 받고 싶은 형식 | Accept: text/html |
┌─────────────────────────────┐
│ Start Line │ ← 첫 줄: 무엇을 할지/결과가 뭔지
├─────────────────────────────┤
│ Headers │ ← 부가 정보 (봉투에 적는 정보)
│ (여러 줄 가능) │
├─────────────────────────────┤
│ (빈 줄) │ ← Header와 Body 구분선
├─────────────────────────────┤
│ Body │ ← 실제 내용물 (편지 내용)
│ (선택적) │
└─────────────────────────────┘
편지 비유로 완전히 이해하기
CRUD는?
| CRUD | HTTP Method | 의미 | 식당 비유 |
|---|---|---|---|
| Create | POST | 새로운 데이터 생성 | 새 메뉴 주문하기 |
| Read | GET | 데이터 조회 | 메뉴판 보기 |
| Update | PUT / PATCH | 데이터 수정 | 주문 변경하기 |
| Delete | DELETE | 데이터 삭제 | 주문 취소하기 |
GET은 "가져오다"라는 의미 그대로 서버에서 데이터를 읽어오는 요청입니다.
# GET 요청 예시
# /users/1 경로에서 ID가 1인 사용자 정보를 조회
GET /users/1 HTTP/1.1
Host: [api.example.com](http://api.example.com)
GET의 특징
?name=abc&age=10)POST는 "게시하다"라는 의미로, 서버에 새로운 데이터를 생성하는 요청입니다.
# POST 요청 예시
# /users 경로에 새로운 사용자 "홍길동"을 생성
POST /users HTTP/1.1
Host: http://api.example.com
Content-Type: application/json
{
"name": "홍길동", # 사용자 이름
"email": "abc@example.com" # 이메일 주소
}
PUT은 "놓다, 두다"라는 의미로, 해당 위치에 데이터를 완전히 대체합니다.
# PUT 요청 예시
# ID가 1인 사용자의 모든 정보를 새로운 값으로 교체
PUT /users/1 HTTP/1.1
Host: http://api.example.com
Content-Type: application/json
{
"name": "abc", # 이름을 abc로 변경
"email": "newemail@example.com", # 이메일도 변경
"age": 28 # 나이 정보 추가
}
PATCH는 "패치, 수선하다"라는 의미로, 데이터의 일부분만 수정합니다.
# PATCH 요청 예시
# ID가 1인 사용자의 이메일만 부분 수정
PATCH /users/1 HTTP/1.1
Host: http://api.example.com
Content-Type: application/json
{
"email": "updated@example.com" # 이메일만 변경, 나머지는 유지
}
DELETE는 "삭제하다"라는 의미 그대로, 서버의 데이터를 제거합니다.
# DELETE 요청 예시
# ID가 1인 사용자 정보를 삭제
DELETE /users/1 HTTP/1.1
Host: http://api.example.com
| 구분 | PUT | PATCH |
|---|---|---|
| 수정 범위 | 전체 교체 | 부분 수정 |
| 보내야 할 데이터 | 모든 필드 | 변경할 필드만 |
| 비유 | 책 전체를 새로 인쇄 | 오탈자만 수정 |
GraphQL은 API를 위한 쿼리 및 조작 언어입니다. GraphQL은 데이터 요구 사항과 상호 작용을 설명하는 유연하고 직관적인 구문을 제공합니다. 이를 통해 개발자는 필요한 것을 정확하게 요청하고 예측 가능한 결과를 얻을 수 있습니다. 또한 단일 요청으로 여러 소스에 접근할 수 있어 네트워크 호출 횟수와 대역폭 요구량을 줄여 배터리 수명과 애플리케이션의 CPU 사용량을 절감할 수 있습니다.
REST는 REpresentational State Transfer의 약자입니다.
| 단어 | 의미 |
|---|---|
| Representational | 표현적인, 대표하는 |
| State | 상태 |
| Transfer | 전송, 이동 |
직역하면 "상태의 표현을 전송한다"입니다.
API는 Application Programming Interface의 약자입니다.
| 단어 | 의미 |
|---|---|
| Application | 응용 프로그램 |
| Programming | 프로그래밍 |
| Interface | 접점, 연결 지점 |
Interface(인터페이스)는 두 시스템이 만나는 접점을 의미합니다.
REST API는 REST 원칙을 따르는 API입니다.
웹에서 클라이언트와 서버가 HTTP 프로토콜을 사용하여 데이터를 주고받을 때 일관된 규칙을 따르도록 설계된 인터페이스입니다.
Github docs, json docs
https://api.example.com:443/users/hgd?role=admin#profile
└──┬─┘ └──────┬───────┘ └┬┘└────┬────┘└────┬────┘└──┬───┘
스킴 호스트 포트 경로 쿼리 프래그먼트
| 구성 요소 | 예시 | 설명 |
|---|---|---|
| 스킴(Scheme) | https,http | 통신 프로토콜 (http, https, ftp 등) |
| 호스트(Host) | api.example.com | 서버의 주소 (도메인 또는 IP) |
| 포트(Port) | 443 | 서버의 출입문 번호 (생략 시 기본값 사용) |
| 경로(Path) | /users/hgd | 자원의 위치 |
| 쿼리(Query) | ?role=admin | 추가 조건 (필터링, 정렬 등) |
| 프래그먼트(Fragment) | #profile | 페이지 내 특정 위치 |
URL은 자원(Resource)을 나타내므로 명사를 사용합니다.
| ❌ 잘못된 예 | ✅ 올바른 예 |
|---|---|
/getUsers | /users |
/createUser | /users (POST 메서드 사용) |
/deleteUser/1 | /users/1 (DELETE 메서드 사용) |
자원은 일반적으로 컬렉션(모음)을 나타내므로 복수형을 사용합니다.
| ❌ 잘못된 예 | ✅ 올바른 예 |
|---|---|
/user | /users |
/book | /books |
/order | /orders |
자원 간의 관계는 URL 경로로 표현합니다.
/users/abc/orders # abc의 주문 목록
/users/abc/orders/123 # abc의 123번 주문
/users/abc/orders/123/items # abc의 123번 주문에 포함된 상품들
| 규칙 | ❌ 잘못된 예 | ✅ 올바른 예 |
|---|---|---|
| 소문자 사용 | /Users, /USERS | /users |
| 하이픈(-) 사용 | /user_orders, /userOrders | /user-orders |
응답 형식은 HTTP 헤더로 지정합니다.
| ❌ 잘못된 예 | ✅ 올바른 예 |
|---|---|
/users/hgd.json | /users/hgd |
/reports/2024.xml | /reports/2024 |
REST를 창시한 Roy Fielding은 6가지 제약 조건을 정의했습니다.
| 제약 조건 | 설명 | 현실적 어려움 |
|---|---|---|
| Client-Server | 클라이언트와 서버의 분리 | ✅ 대부분 지킴 |
| Stateless | 서버가 클라이언트 상태를 저장하지 않음 | ⚠️ 세션, 인증 토큰 등 사용 |
| Cacheable | 응답은 캐시 가능해야 함 | ⚠️ 캐시 설계 복잡 |
| Uniform Interface | 일관된 인터페이스 | ❌ HATEOAS 거의 미적용 |
| Layered System | 계층화된 시스템 | ✅ 대부분 지킴 |
| Code on Demand | 필요시 코드 다운로드 (선택) | ➖ 거의 사용 안 함 |
REST API의 가장 중요한 원칙 중 하나는 Resource(자원)와 Verb(행위)를 분리하는 것입니다.
| 구분 | 설명 | 예시 |
|---|---|---|
| Resource (자원) | 다루고자 하는 대상 (명사) | 사용자, 게시글, 댓글 |
| Verb (행위) | 자원에 대해 수행할 동작 (동사) | 조회, 생성, 수정, 삭제 |
왜 분리해야 할까요?
일상생활에서 우리는 자연스럽게 명사와 동사를 분리합니다.
URL에서도 마찬가지입니다!
/users(명사) + GET(동사) = 사용자 목록 조회/users(명사) + POST(동사) = 사용자 생성/users/2(명사) + GET(동사) = 사용자 중 2번 ID를 가진 사람을 조회JSON은 JavaScript Object Notation의 약자로, 데이터를 저장하고 전송하기 위한 텍스트 기반의 데이터 형식입니다.
JSON도 "키": "값" 형태로 데이터를 정리합니다.
| 택배 송장 | JSON |
|---|---|
| 받는 사람: 홍길동 | "name": "홍길동" |
| 주소: 서울시 강남구 | "address": "서울시 강남구" |
| 연락처: 010-1234-5678 | "phone": "010-1234-5678" |
// JSON의 기본 구조
// 중괄호 {} 안에 "키": 값 쌍을 작성합니다
// 키는 반드시 큰따옴표(")로 감싸야 합니다
// 여러 항목은 쉼표(,)로 구분합니다
{
"name": "hgd",
"age": 30,
"isInstructor": true
}
| 타입 | 설명 | 예시 |
|---|---|---|
| 문자열 (String) | 텍스트 데이터, 큰따옴표로 감싸야 함 | "홍길동" |
| 숫자 (Number) | 정수 또는 소수, 따옴표 없이 작성 | 25, 3.14 |
| 불리언 (Boolean) | 참/거짓 값 | true, false |
| null | 값이 없음을 표현 | null |
| 배열 (Array) | 여러 값의 순서 있는 목록 | [1, 2, 3] |
| 객체 (Object) | 키-값 쌍의 집합 | {"a": 1} |
※ 키 중복값은 X
// 한 사람의 정보를 담은 JSON 객체
// 이름, 직업, 나이 정보를 키-값 쌍으로 표현합니다
{
"name": "홍길동",
"job": "도사",
"age": 31
}
// 여러 개의 값을 순서대로 나열할 때 배열을 사용합니다
// 대괄호 [] 안에 값들을 쉼표로 구분하여 작성합니다
{
"students": ["김철수", "이영희", "박민수"]
}
// 객체 안에 또 다른 객체나 배열을 포함할 수 있습니다
// 이를 통해 복잡한 데이터 구조를 표현합니다
{
"instructor": {
"name": "hgd",
"koreanName": "홍길동",
"subjects": ["Python", "Git", "AWS"]
},
"studentCount": 25,
"isActive": true
}
| 함수 | 방향 | 입력 | 출력 | 용도 |
|---|---|---|---|---|
json.dumps() | 직렬화 | Python 객체 | JSON 문자열 | 네트워크 전송, 로그 출력 |
json.dump() | 직렬화 | Python 객체 | 파일에 저장 | 데이터 영구 저장 |
json.loads() | 역직렬화 | JSON 문자열 | Python 객체 | API 응답 처리 |
json.load() | 역직렬화 | 파일 | Python 객체 | 저장된 데이터 불러오기 |
import json
# ============================================================
# 1. 기본 직렬화: 파이썬 딕셔너리를 JSON 문자열로 변환
# ============================================================
# hgd 사용자 정보를 담은 딕셔너리 생성
user_data = {
"name": "hgd", # 문자열 값
"email": "hgd@example.com",
"age": 30, # 정수 값
"is_active": True, # 불리언 값 (JSON에서는 true로 변환됨)
"skills": ["Python", "JavaScript", "SQL"] # 리스트 값
}
# json.dumps(): 딕셔너리 → JSON 문자열
# dumps의 's'는 string을 의미합니다
json_string = json.dumps(user_data)
print(type(json_string)) # <class 'str'> - 문자열 타입
print(json_string)
# 출력: {"name": "hgd", "email": "hgd@example.com", "age": 30, ...}
한글 데이터를 다룰 때는 항상 ensure_ascii=False를 사용하세요.
디버깅 시에는 indent=2 또는 indent=4를 함께 사용하면 좋습니다.
import json
# hgd의 설정 정보
config = {
"user": "hgd",
"theme": "dark",
"language": "ko",
"notifications": True
}
# ============================================================
# json.dump(): 파이썬 객체를 파일에 직접 저장
# ============================================================
# 's'가 없는 dump는 파일 객체를 받습니다
# 파일 열기 (쓰기 모드, UTF-8 인코딩)
with open("config.json", "w", encoding="utf-8") as file:
# dump(저장할_데이터, 파일_객체, 옵션들...)
json.dump(config, file, ensure_ascii=False, indent=4)
print("설정이 config.json 파일에 저장되었습니다!")
import json
# ============================================================
# 서버에서 받은 JSON 응답 (문자열 형태)
# ============================================================
# 실제로는 API 호출 결과로 이런 문자열을 받게 됩니다
json_response = '''
{
"status": "success",
"data": {
"user_id": 1,
"username": "hgd",
"email": "hgd@cloudschool.com"
},
"message": "로그인 성공"
}
'''
# ============================================================
# json.loads(): JSON 문자열 → 파이썬 딕셔너리
# ============================================================
# loads의 's'는 string을 의미합니다
result = json.loads(json_response)
# 이제 파이썬 딕셔너리처럼 사용할 수 있습니다!
print(type(result)) # <class 'dict'>
print(result["status"]) # success
print(result["data"]["username"]) # hgd
import json
# ============================================================
# json.load(): 파일에서 직접 읽어서 파이썬 객체로 변환
# ============================================================
# 's'가 없는 load는 파일 객체를 받습니다
# 파일 열기 (읽기 모드, UTF-8 인코딩)
with open("config.json", "r", encoding="utf-8") as file:
# load(파일_객체) - 파일 내용을 읽어서 파이썬 객체로 변환
loaded_config = json.load(file)
print(f"사용자: {loaded_config['user']}") # hgd
print(f"테마: {loaded_config['theme']}") # dark
print(f"언어: {loaded_config['language']}") # ko
직렬화란 복잡하게 얽힌 데이터 구조를 일렬(Serial)로 펼쳐서 저장하거나 전송할 수 있는 형태로 만드는 것입니다.
마치 입체 퍼즐을 분해해서 한 줄로 나열하는 것과 같습니다.