본문으로 건너뛰기
데이터는 함수에서 객체로, 객체에서 개체로 간다

생성 수정

IBM * 레드햇 AX 과정 · 7강

데이터는 함수에서 객체로, 객체에서 개체로 간다

2026년 5월 18일(월) ~ 5월 22일(금)

이 강의 문제풀이 — 배운 내용을 주관식 문답으로 확인해보세요.풀어보기 →

이 장을 다 읽으면

  1. 화면에서 입력한 글자 하나가 데이터베이스에 저장되기까지 지나는 세 계층과 두 연결고리를 순서대로 말할 수 있다.
  2. 선언 · 정의 · 정제 · 파인튜닝을 각각 "언제 일어나는 일"인지로 구분할 수 있다.
  3. 괄호 () 하나로 기능과 속성을 판별할 수 있고, 그 구분이 프론트·백엔드·DB에서 어떤 이름으로 불리는지 안다.
  4. 무상태(Stateless)가 무엇에 대한 말인지 정확히 말할 수 있다 — 프론트도 백엔드도 상태를 가진다는 사실을 포함해서.
  5. GET과 POST를 "서버의 원본이 바뀌는가"라는 축으로 구분하고, REST API의 3대 구성요소를 댈 수 있다.
  6. DOM과 JSON이 왜 본질적으로 같은 일을 하는지 설명할 수 있다.
  7. UI = f(state) 공식으로 리액트 컴포넌트를 읽고, e.preventDefault() 가 실제로 막는 것이 무엇인지 안다.
  8. 클로저가 왜 "상태를 가지는 함수"인지를 스택·힙·가비지 컬렉션으로 설명할 수 있다.
  9. 식별관계와 비식별관계를 구분하고, 그것이 앞에서 배운 실선·점선과 같은 질문임을 안다.

0. 이 장의 판별 축

앞에서 배운 대로, 새 용어를 만나면 비유를 찾기 전에 아래 축 중 어디에 꽂히는지부터 정하세요.

#축왼쪽 (0)오른쪽 (1)이 축으로 갈리는 것
1상태를 안에 들고 있는가무상태 (Stateless)상태 (Stateful)함수 ↔ 클래스 / HTML ↔ State / 통신 구간 ↔ 양쪽 끝
2이름만인가, 알맹이까지인가선언 (Declaration)정의 (Definition)let password; ↔ CREATE TABLE ...
3괄호가 있는가없음 → 속성있음 → 기능data.message ↔ alert()
4변하는가정적 (Static)동적 (Dynamic)Attribute ↔ Property / 클래스변수 ↔ 인스턴스변수 / 상수 ↔ 변수
5서버의 원본이 바뀌는가Safe (GET)Unsafe (POST·PUT·DELETE)조회 ↔ 조작
6부모 없이 존재할 수 있는가못 함 → 식별관계가능 → 비식별관계실선 ↔ 점선

이 중 축 1이 이 장의 중심입니다. 이 축 하나로 함수·클래스·HTTP·리액트·클로저가 전부 한 줄에 꿰집니다.

축 4는 앞에서 배운 3주차의 축 1(디스크 ↔ 메모리)이 한 겹 더 세분화된 것입니다. 축 6은 3주차 3.2절의 실선/점선(연관관계·의존관계) 이 데이터베이스 쪽에서 회수되는 자리입니다. "앞으로 다시 만납니다"라고 예고해 둔 그 약속을 이번 장 10절에서 지킵니다.


1. 이 주의 큰 그림 — 함수에서 객체로, 객체에서 개체로

1.1 3주차의 좌표평면이 그대로 배치도가 된다

앞에서 배운 뺄셈식을 다시 꺼냅니다.

객체 (Object)   = 속성 + 기능
개체 (Entity)   = 객체 − 기능      →  저장만 된다
함수 (Function) = 객체 − 속성      →  변화만 일으킨다

3주차에서는 이 셋이 무엇인지를 배웠습니다. 이번 주에는 이 셋이 어디에 놓이고 어떤 순서로 이어지는지를 배웁니다. 같은 개념이지만, 이제는 관념이 아니라 실제로 데이터가 흘러가는 공장의 세 공정입니다.

[1단계: 수집 및 정형화]        [2단계: 정제 및 로직 연산]        [3단계: 영구 저장 및 관리]

  함수 (Frontend / HTML)  ──▶   객체 (Backend / Python)  ──▶   개체 (Database / SQL)

  - Raw Data 수집                - AI 모델 구동                  - 영구 보관 (Persistence)
  - 입력 데이터 규격화            - 메모리 상의 객체 제어           - 테이블 구조화

이 한 줄이 4주차 전체의 뼈대입니다. HTML(함수)로 사용자의 Raw Data를 정형화해 입력받고, Python(객체)으로 정제·연산한 뒤, SQL(개체)로 영구 저장한다.

1.2 세 계층이 각각 하는 일

① 함수 (Frontend / HTML, JS) — 데이터의 수집과 정형화

  • 범주: 시스템의 최전방에서 사용자와 직접 상호작용하는 인터페이스(Interface)이자 입구입니다.
  • 하는 일: 사용자의 행동(클릭, 입력, 센싱)으로 발생하는 가공되지 않은 날것의 데이터(Raw Data) 를 받아, 서버가 인식하고 연산할 수 있는 규격화된 포맷(JSON, Key-Value)으로 정형화합니다.
  • 왜 함수인가: 자바스크립트/타입스크립트 함수가 작동해서, 결과물(return)로 최종 HTML 구조를 화면에 그립니다. 속성을 안에 쌓아두지 않고 변화만 일으키므로 함수입니다.
  • 비유: 외부에서 들여온 원료를 검수하기 좋게 일정한 크기로 1차 분류하고 포장하는 공정.

② 객체 (Backend / Python) — 데이터의 정제와 제어

  • 범주: 넘어온 정형화된 데이터를 받아 핵심 비즈니스 로직을 수행하고 AI 모델을 구동하는 시스템의 두뇌입니다.
  • 하는 일: 규격화된 데이터를 프로그램 내부에서 연산·조작할 수 있도록 정제하여, 가상 메모리 공간 위의 객체(Object) 형태로 제어합니다.
  • 왜 파이썬인가: 무겁고 복잡한 컴파일 과정 없이 즉각적인 수정과 처리가 가능한 스크립트 언어이기 때문입니다. AI 라이브러리 생태계가 여기 모여 있는 것도 같은 이유입니다.
  • 비유: 1차 포장된 원료를 조립·검수해 가치 있는 최종 완성품(객체)으로 만드는 핵심 생산 라인.

③ 개체 (Database / SQL) — 데이터의 영구 저장과 관리

  • 범주: 정제가 완료된 데이터를 유실되지 않도록 안전하고 영구적으로 보관하는 데이터 창고입니다.
  • 하는 일: 파이썬 휘발성 메모리에 존재하던 객체 데이터를 SQL 질의어로 관계형 데이터베이스(RDB) 안의 구조화된 테이블, 즉 개체(Entity) 로 영구 저장(Persistence) 합니다.
  • 비유: 출고된 완성품을 창고의 지정된 선반(Table)에 격자로 맞춰 정확한 라벨(Primary Key)을 붙여 쌓아두는 물류 시스템.

1.3 두 개의 연결고리 — API와 커넥션

세 계층 사이에는 연결고리가 두 개 있습니다. 이 둘을 별도로 이름 붙여 두는 것이 중요합니다. 계층만 외우면 "그 사이는 어떻게 넘어가지?"에서 항상 막히기 때문입니다.

[ Frontend ]          [ 연결고리 A ]         [ Backend ]          [ 연결고리 B ]         [ Database ]

  f(x) (HTML)   ◀──  API (GET/POST)  ──▶   오브젝트 (Python)  ◀──  커넥션 (ORM)  ──▶   엔티티 (SQL)

⛓️ 연결고리 A: API (Application Programming Interface)

  • 역할: 프론트엔드(함수)와 백엔드(객체) 사이의 통신 규약입니다.
  • 메커니즘: 정형화된 데이터 패키지를 POST 로 백엔드에 쏘아 보내거나, 반대로 GET 으로 서버의 가공된 결과 데이터를 받아옵니다.
  • 앞에서 배운 물류 비유로는 선로(Track) 입니다. Axios가 기관차, JSON이 화물, REST가 교통 법규였습니다.

⛓️ 연결고리 B: 커넥션 (DB Connection / ORM)

  • 역할: 백엔드 프로그램과 데이터베이스 엔진 사이의 물리적·논리적 연결 통로입니다.
  • 메커니즘: 파이썬 메모리에만 존재하는 휘발성 객체를 데이터베이스에 유실 없이 전달하기 위해 드라이버 회선을 활성화합니다. 이것을 자동화한 기술이 ORM(Object-Relational Mapping) 입니다.
  • 3주차에 Neon에 붙일 때 쓴 create_async_engine, AsyncSession, get_db 가 전부 이 연결고리를 만드는 코드였습니다.

⭐ 연결고리는 계층이 아니라 "다리"입니다. 계층은 데이터가 머무는 곳이고, 연결고리는 데이터가 모양을 바꾸며 건너가는 곳입니다. 버그의 대부분은 계층 안이 아니라 이 두 다리 위에서 생깁니다.

1.4 아키텍처 매트릭스

아키텍처 레이어실제 기술 스택데이터 상태 및 이동 수단핵심 설계 패러다임
함수 (f(x))HTML5 / JavaScript (React)Raw Data 수집 및 1차 정형화이벤트 주도 개발(EDA) / 사용자 인터페이스 제어
통신 인터페이스 (A)API (GET / POST)JSON 텍스트 기반 데이터 전송HTTP 프로토콜 규격화 / RESTful 아키텍처
객체 (Object)Python / FastAPI / AI 모델가상 메모리 상의 오브젝트 정제1급 객체(First-Class) 활용 및 백엔드 로직 연산
데이터 드라이버 (B)DB Connection / ORMSQL Query 문맥 변환 전달데이터 변환 파이프라인 동기화
개체 (Entity)SQL / Relational DB (RDB)구조화된 테이블 내 영구 저장데이터 무결성 보장 및 영속성(Persistence) 유지

최종 메커니즘 요약 사용자가 화면(HTML)에서 행위를 일으키면, 프론트엔드 함수가 이를 감지해 데이터를 정형화한 뒤 POST API 엔드포인트로 보냅니다. 백엔드(Python)는 이를 받아 메모리 공간의 객체로 적재하고 AI 연산으로 심층 정제합니다. 정제된 데이터는 전용 커넥션을 타고 이동하여, SQL 질의어를 통해 격자형 테이블인 개체(Entity) 에 영구 저장됩니다.

1.5 왜 형태가 세 번이나 바뀌는가

3주차 8.1절에서 "각 경계를 넘을 때 데이터의 모양이 달라진다"고 배웠습니다. 그 이유를 이제 정확히 말할 수 있습니다.

계층데이터가 있어야 하는 모습그 모습이어야 하는 이유
프론트엔드텍스트·이벤트사람이 손으로 입력하고 눈으로 보기 때문
통신 구간JSON 텍스트언어가 서로 달라도 읽을 수 있어야 하기 때문
백엔드메모리 객체계산하고 조작하려면 기능이 붙어 있어야 하기 때문
데이터베이스테이블 행전원이 꺼져도 남아야 하고, 검색·정렬이 되어야 하기 때문

각 계층이 요구하는 조건이 서로 다릅니다. 한 가지 형태로 끝까지 갈 수 없기 때문에 형태가 바뀌는 것이지, 개발자가 일을 복잡하게 만드는 것이 아닙니다.


2. 네 개의 단어 — 선언 · 정의 · 정제 · 파인튜닝

이 네 단어는 전부 "데이터를 다룬다"는 점에서 비슷해 보이지만, 일어나는 시점이 완전히 다릅니다. 시점으로 외우면 절대 안 헷갈립니다.

2.1 디클러레이션 (Declaration : 선언) — 자리를 예약하는 일

  • 정의: 디클러레이션은 [구조 설계 행위]인데, [식별자(이름)만 등록하고 알맹이는 아직 비워둔다]는 점에서 정의(Definition)와 구별된다.
  • 컴퓨터에게 하는 말: "내가 앞으로 이런 이름을 가진 변수나 함수를 쓸 테니, 가상 메모리 공간에 자리를 준비해 둬라."
  • 아키텍처적 의미: 실제 데이터의 형태나 내부 로직(Body)은 아직 채워지지 않았습니다. 단지 '이름'만 컴파일러나 인터프리터에 등록하는 메모리 할당 예약 단계입니다.
  • 예시: 자바스크립트의 let password; / C·C++에서 함수의 프로토타입(껍데기)만 명시하는 행위.
let password;          // 선언 — 이름표만 붙였다. 안에는 아무것도 없다.

2.2 데피니션 (Definition : 정의) — 알맹이를 확정하는 일

  • 정의: 데피니션은 [구조 설계 행위]인데, [데이터 형식과 내부 실행 코드·규칙까지 확정한다]는 점에서 선언과 구별된다.
  • (Define은 동사형, Definition은 명사형입니다. 본질은 같습니다.)
  • 아키텍처적 의미: 시스템의 뼈대와 규격을 완성하는 단계입니다. 데이터베이스 설계 시 테이블의 제약 조건(PK, FK)과 데이터 타입을 확정 짓는 스키마 구조 생성이 여기 해당합니다.
  • 예시:
CREATE TABLE User (id INT, email VARCHAR(255));   -- 정의 — 형식과 규칙이 확정됨
def chat(msg):          # 여기까지가 선언에 가깝고
    return llm(msg)     # 이 알맹이가 채워져야 정의가 완성됩니다

2.3 리파인 (Refine : 정제) — 들어온 물을 거르는 일

  • 정의: 리파인은 [데이터 전처리 행위]인데, [이미 유입된 Raw Data에서 불순물을 걷어낸다]는 점에서, 구조를 만드는 선언·정의와 구별된다.
  • 하는 일: 수집된 불완전한 날것의 데이터에서 노이즈(Noise), 결측치(Null), 오타, 불필요한 공백, 중복 요소 등을 걸러내어 가치 있는 정형 데이터로 다듬습니다.
  • 어디서 일어나나: 프론트엔드 함수 단계의 1차 검증(Validation)·규격화, 그리고 백엔드 파이썬 환경(Pandas 등)에서의 데이터 클렌징을 통칭합니다.
  • 예시: 사용자가 입력한 이메일 앞뒤의 공백을 잘라내는 로직(trimmedEmail), 유효하지 않은 특수문자를 필터링하는 파이프라인.

2.4 파인튜닝 (Fine-Tuning : 미세 조정) — 모델의 가중치를 손보는 일

  • 정의: 파인튜닝은 [모델 최적화 행위]인데, [데이터가 아니라 AI 모델의 가중치(Weights)를 바꾼다]는 점에서 앞의 셋과 완전히 구별된다.
  • 하는 일: 이미 대규모 데이터로 사전 학습(Pre-training)을 마친 기본 모델(Foundation Model)에, 특정 목적·도메인(의료·법률·금융·사내 데이터)에 맞게 정제된 소량의 데이터를 추가 학습시켜 가중치를 미세 조정합니다.
  • 아키텍처적 의미: 일반 상식이 뛰어난 범용 AI에게 우리 비즈니스에 특화된 전문 지식을 이식하는 과정입니다. 데이터베이스(개체)에 누적된 고품질 정제 데이터를 기반으로, 파이썬 백엔드 환경에서 수행됩니다.
  • 예시: 오픈소스 LLM에 우리 회사의 제품 매뉴얼과 상담 로그를 추가 학습시켜 "사내 전문 상담 챗봇"으로 고도화하는 것.

2.5 네 단어 일람표

기술 용어핵심 기능데이터 상태의 변화시스템 내 실행 시점
디클러레이션 (Declaration)변수/함수 선언메모리 주소(공간) 예약 상태소스 코드 컴파일 / 인터프리트 초기
디파인 / 데피니션 (Definition)구조/로직 정의데이터 형식 및 테이블 스키마 확정DB 빌드 및 소스 코드 함수 구현 시점
리파인 (Refine)데이터 정제Raw Data → 정형 데이터 변환프론트 입력 핸들러 및 백엔드 전처리 파이프라인
파인튜닝 (Fine-Tuning)AI 모델 미세조정AI 모델의 가중치(Parameter) 고도화정제 데이터 축적 후 백엔드 AI 학습 스케줄러

유기적 연결 디클러레이션과 데피니션으로 화면 구조와 테이블 스키마를 정교하게 설계해 둡니다. 사용자의 행동으로 유입된 Raw Data를 리파인하여 불순물 없는 객체로 바꿉니다. 이렇게 무결성이 검증되어 저장된 고품질 데이터로 AI 모델을 파인튜닝하여 서비스의 지능을 고도화합니다.

여기서 앞의 두 개(선언·정의)는 틀을 만드는 일이고, 뒤의 두 개(정제·파인튜닝)는 틀에 들어온 것을 다듬는 일입니다. 이 "틀과 내용물"의 구분은 이 장 전체에서 계속 반복됩니다(3.2절, 11.2절).

2.6 선언과 정의를 다시 — 이름표와 알맹이

같은 구분을 비전공자 언어로 한 번 더 붙잡아 둡시다. 새 가전제품을 기획하고 출시하는 과정과 같습니다.

선언 (Declaration)정의 (Definition)
말하자면"앞으로 'AI 청소기'라는 제품을 만들 거야!""흡입력은 얼마고, 먼지통은 이렇게 비운다."
하는 일기획서에 이름만 올려놓는 것공장에서 실제 기능과 알맹이를 조립하는 것
코드에서머리(Header) 부분몸통(Body) 부분
컴퓨터에게"이 이름을 쓸 거니까 기억해 둬""이 이름은 실제로 이렇게 동작한다"

한 줄 요약: 이름표를 붙이는 것은 선언, 실제 알맹이를 만드는 것은 정의입니다.


3. 괄호 하나가 가르는 것 — 기능과 속성

3.1 괄호 () 가 있으면 기능, 없으면 속성

앞에서 배운 축 2(속성 ↔ 기능) 를, 코드를 눈으로 볼 때 1초 만에 판별하는 방법이 있습니다.

"괄호 () 가 있으면 움직이는 '기능', 없으면 멈춰있는 '속성'"

TV 리모컨을 떠올리면 쉽습니다.

괄호가 있으면: 기능 (함수 / 메서드)

  • 비유: 리모컨의 '버튼을 누르는 행동' 입니다. 괄호 () 는 버튼을 꾹 누르는 모양이라고 생각하세요. 누르면 화면이 켜지거나 소리가 커지는 '동작' 이 일어납니다.
  • 코드: window.alert("환영합니다") → 경고창을 띄우는 '행동'을 수행하라는 뜻.

괄호가 없으면: 속성 (변수 / 필드 / 데이터)

  • 비유: 리모컨의 '상태나 스펙' 입니다. 버튼을 누른다고 리모컨 색이 검은색에서 빨간색으로 변하지 않듯, 그저 '검은색', '배터리 잔량 80%'라는 '값(데이터)' 자체를 머금고 있습니다.
  • 코드: data.message → 데이터 안에 적혀 있는 '글자 내용' 그 자체.

⚠️ 주의: 괄호가 있는데 아무것도 안 넣는 경우(get_data())도 기능입니다. 괄호 안에 뭐가 들었는지가 아니라, 괄호가 있는지 없는지가 기준입니다.

3.2 외부 골격(상수) ↔ 내부(변수) — 틀과 물

기능/속성과 짝을 이루는 또 하나의 구분입니다.

"단단한 그릇(상수) 안에, 언제든 바뀔 수 있는 물(변수)을 담는다"

웹사이트의 '이름 입력창'을 생각해 보세요.

외부 골격: 상수 (const / Constant)내부: 변수 (Variable / State)
무엇인가화면에 고정된 '이름을 입력하세요' 회색 입력 틀(그릇)그 틀 안에 사용자가 타이핑하는 '홍길동', '이순신' 같은 실제 이름(물)
언제 바뀌나회원가입이 끝날 때까지 위치·크기·디자인이 변하지 않음글자를 지우면 사라지고 다른 글자로 계속 바뀜
성격정적(Static)동적(Dynamic)

한 줄 요약 코딩은 고정된 틀(상수) 안에 바뀌는 데이터(변수) 를 넣고, 그것들의 태생(선언/정의) 을 밝힌 뒤, 괄호를 눌러 행동(기능) 하게 하거나 상태를 읽는 값(속성) 으로 이루어져 있습니다.

앞으로 다시 만납니다 이 "틀 = 상수 / 알맹이 = 변수"는 이 장 11절에서 4대 아키텍처 구조 전체를 설명하는 하나의 렌즈로 다시 나옵니다. 그리고 9절에서 정적 ↔ 동적 3계층 매핑표로 정밀화됩니다.

3.3 같은 것을 다르게 부르는 세 영역

속성(attribute / property / field) 은 영역마다 부르는 이름과 정의가 미묘하게 다릅니다. 이 표를 붙잡아 두지 않으면, 같은 것을 다른 이름으로 배울 때마다 새로 배우는 느낌이 듭니다.

프론트엔드 (함수형)백엔드 (객체지향)DB (SQL)
큰 단위컴포넌트 (Component)클래스 (Class)테이블 (Table)
속성Attribute / Property / State필드(Field) / 멤버 변수컬럼(Column) / 속성(Attribute)
기능함수 / 화살표 함수 / 핸들러메서드(Method)(없음 — 개체는 기능이 없음)
한 건props로 렌더된 한 화면인스턴스(Instance)행(Row) / 레코드
패러다임무상태(Stateless) 지향객체지향(OOP)관계 모델(Relational)

수업에서 여러 번 강조한 대응은 이렇습니다.

프론트엔드백엔드
함수클래스
functionclass
화살표 함수 (=>)메서드
constdef

⚠️ 이건 "완전히 같다"는 뜻이 아니라 "같은 자리를 차지한다"는 뜻입니다. 프론트에서 function 이 담당하는 역할의 자리를 백엔드에서는 class 가 차지합니다. 그래서 프론트 코드를 읽을 때 "이건 백엔드로 치면 뭐지?"라고 물으면 구조가 빨리 보입니다.


4. 무상태와 상태 — 이 장에서 가장 많이 오해받는 개념

4.1 두 패러다임 — 무상태 프론트엔드, 객체지향 백엔드

시스템의 앞뒤는 서로 다른 사고방식으로 만들어집니다.

프론트엔드 (클라이언트)백엔드 (서버)
중심 패러다임무상태(Stateless) 패러다임객체지향(OOP) 패러다임
설계 방식과거의 기억이나 문맥 없이, 현재 들어온 데이터(State)를 그대로 화면에 독립적으로 렌더링데이터와 비즈니스 로직을 '객체'라는 단위로 쪼개어 관리
예UI = f(state) — 상태를 넣으면 화면이 나옴User, Order, Sensor 객체가 서로 협력
단,—통신할 때는 확장성을 위해 주로 무상태 방식을 채택

이 문장의 마지막 줄이 중요합니다. 여기가 오해의 출발점입니다.

4.2 ⭐ Stateless 오해 바로잡기 — 무상태인 건 통신 구간뿐이다

가장 흔한 오해는 이것입니다.

❌ 틀린 이해: "프론트엔드는 무상태니까 아무것도 기억하지 않는다." ❌ 틀린 이해: "REST를 쓰면 서버는 상태를 저장하지 않는다."

두 문장 모두 틀렸습니다. 프론트도 백엔드도 상태를 가집니다.

   [ F. 프론트엔드 ]          ← 여기는 STATEFUL
        다크 모드 설정
        장바구니 목록
        입력창 텍스트
             │
             │  ◀────── 이 구간만 STATELESS ! (REST API 통신)
             │           서버는 이전 요청을 기억하지 않는다
             ▼
   [ B. 백엔드 & 데이터베이스 ]  ← 여기도 STATEFUL
        회원 정보
        게시글
        영구적 데이터(State)
영역상태를 가지는가무엇을 가지고 있나
프론트엔드(클라이언트)✅ 가집니다화면을 바꾸기 위한 많은 상태 — 다크 모드, 장바구니, 입력창 텍스트
통신 구간(REST API)❌ 안 가집니다여기만 무상태입니다
백엔드 & 데이터베이스✅ 가집니다회원 정보, 게시글 등 영구적 데이터

⭐ 핵심 요약 각자가 상태를 가지지 않는 것이 아니라, 서로 통신을 주고받을 때 서버가 클라이언트의 상태를 기억해 주지 않는 것입니다.

대화로 옮기면 이렇습니다.

서버의 태도
✗ Stateful 통신"아까 그 사람이지?" — 이전 요청을 기억함
○ Stateless 통신"신분증 보여줘." — 매 요청마다 확인함

그래서 클라이언트는 매 요청마다 필요한 모든 정보(인증 토큰 등)를 새로 담아 보내야 합니다. 이것이 나중에 배울 JWT(JSON Web Token) 가 존재하는 이유입니다. 서버가 안 기억해 주니까, 클라이언트가 신분증을 매번 들고 가는 것입니다.

4.3 왜 통신만 무상태로 만들었는가 — 세 가지 이득

불편해 보이는 설계를 일부러 택한 이유가 있습니다.

이점내용
확장성 (Scalability)서버를 몇 대로 늘려도 됩니다. 어느 서버가 요청을 받든 모두 같은 응답을 하기 때문입니다
안정성 (Reliability)한 서버가 고장 나도 문제없습니다. 그 서버에만 있던 기억이 없으니까요
단순성 (Simplicity)서버 로직이 명확해집니다. "이 사람이 아까 뭘 했더라"를 관리할 필요가 없습니다

만약 서버가 사용자의 상태를 기억한다면, 그 사용자는 반드시 같은 서버로만 가야 합니다. 서버 증설이 곧바로 어려워집니다. 무상태는 불편을 감수하고 확장성을 산 설계입니다.

4.4 회원가입 한 건이 지나가는 전 구간

이 장의 개념을 한 줄로 잇는 가장 좋은 사례입니다. 회원가입 버튼 한 번에 무슨 일이 일어나는지 따라가 봅시다.

[Client 내부]  HTML(속성)과 스크립트(기능)가 만나 입력창의 '상태'를 만듦
      │
      ▼ (버튼 클릭!)
[Request 준비] 데이터를 기억 상실증에 걸린 '무상태' 패키지로 포장함
      │
      ▼ (HTTP 통신 시작)
[하네스 통로]  HTTP Request 규칙에 맞춰 규격화된 통로로 서버에 전송
      │
      ▼ (서버 도착)
[Server 내부]  독립된 모듈들이 패키지를 받아 '객체지향 부품'을 만들고, DB에 '상태'로 저장함

단계별로 다시 보면

  1. 입력창 내부는 상태(State) 입니다. 사용자가 타이핑하는 동안 화면은 그 값을 들고 있습니다.
  2. 버튼을 누르는 순간 "무상태"로 패키징됩니다. 서버가 기억해 줄 것을 전제하지 않고, 필요한 정보를 전부 담습니다.
  3. HTTP 통신은 무상태 요청과 무상태 응답의 규칙을 따릅니다(예: JWT 인증).
  4. 서버에서 "부품(객체)"으로 조립되어, 최종적으로 데이터베이스에 영구 "상태" 로 정착합니다.
구간상태인가 무상태인가왜
프론트엔드 입력창상태사용자가 입력하는 중이므로
전송 패키지무상태서버가 기억해 주지 않으므로 전부 담아야
백엔드 객체상태메모리 위에서 값을 들고 연산해야 하므로
데이터베이스상태영구 보관이 목적이므로

⭐ 정리하면: 양 끝은 상태, 가운데만 무상태. 이 그림 하나면 4.2절의 오해가 다시 생기지 않습니다.

4.5 상태를 어디에 둘 것인가

상태가 여기저기에 있다면, 그 다음 질문은 "어떤 상태를 어디에 두어야 하는가" 입니다.

위치언제 쓰나예시
화면 상태잠깐 쓰는 값검색창 입력, 메뉴 열림/닫힘, 선택된 탭
서버여러 사용자가 공유하거나 처리 판단이 필요한 값로그인 요청 처리, 권한 확인, 주문 금액 계산
DB오랫동안 보관해야 하는 값회원 정보, 게시글, 주문 내역

점검 질문 두 가지가 특히 유용합니다.

  • 잠깐 필요한 값이 DB에 저장되고 있지는 않나?
  • 오래 필요한 값이 화면에만 남아 있지는 않나?

AI에게 요청할 때는 이렇게 명시합니다.

검색창 입력은 화면 상태로 관리하고, 회원 정보는 DB에 저장하며,
로그인 여부는 서버에서 확인하는 구조로 만들어줘.

⚠️ 서버 메모리에 상태를 두는 것은 피합니다. 서버를 늘리는 순간 깨집니다. 상태는 JWT(인증) / Redis(단기) / RDBMS·NoSQL(영속) 에 위임합니다.

4.6 함수는 무상태, 클래스는 상태 — 결정적 기준

같은 축이 코드 수준에서도 그대로 반복됩니다. 함수(f(x))와 클래스(class)를 구분하는 결정적 기준은 "상태(State)를 내부에 저장하고 유지하는가"입니다.

함수 f(x) : 무상태 (Stateless)

  • 동작 방식: 외부에서 입력값(x)을 받으면 정해진 로직을 수행한 후 결과값을 반환(return)하고 역할을 끝냅니다.
  • 메모리 특징: 실행되는 동안에만 임시로 지역 변수를 스택(Stack) 영역에 만들었다가, 반환이 끝나는 즉시 모두 소멸시킵니다.
  • 같은 입력에 대해 언제나 같은 출력을 보장하는 '순수 함수(Pure Function)' 의 지향점이기도 합니다.
  • 비유: 입력값을 받아 정해진 공식대로 결과만 출력하는 계산기나 자판기.

클래스 (class) : 상태 (Stateful)

  • 동작 방식: 클래스로 객체(Instance)를 만들면, 이 객체는 힙(Heap) 영역에 독립된 공간을 차지하며 고유의 멤버 변수(속성) 를 가집니다.
  • 메모리 특징: 객체가 소멸하기 전까지 메모리에 계속 상주합니다. 메서드를 호출할 때마다 내부 변수(상태)가 업데이트될 수 있고, 그 변경된 상태는 다음 호출 때도 그대로 유지됩니다.
  • 비유: 캐릭터를 생성하면 '현재 체력(HP)', '레벨', '경험치'가 계속 유지되고 변화하는 게임 시스템.

비교표

구분함수 (f(x))클래스 (class)
핵심 목적입력값에 대한 특정 연산 및 동작 수행데이터와 행위를 묶어 현실 세계의 객체를 표현
상태 관리무상태 (Stateless) — 실행 종료 후 내부 데이터 소멸상태 유지 (Stateful) — 인스턴스 생명주기 동안 데이터 보존
결과 결정 요소오직 전달된 입력값(x)에 의해서만 결정입력값 + 객체의 현재 내부 상태
메모리 영역StackHeap
주요 패러다임함수형 프로그래밍 (Functional)객체지향 프로그래밍 (OOP)

이 구분은 "언제 함수를 쓰고 언제 클래스로 설계할 것인가"의 나침반입니다. 상태를 들고 있어야 하면 클래스, 들고 있을 필요가 없으면 함수 — 이것이 클린 아키텍처 설계의 첫 갈림길입니다.

⚠️ 그런데 함수도 상태를 가질 수 있습니다. 그 예외가 8절의 클로저입니다. 예외를 미리 알아두면, 8절에 가서 "그럼 앞에서 배운 게 틀린 건가?" 하고 흔들리지 않습니다.


5. HTTP와 REST — 규약의 세계

5.1 프로토콜이란 무엇인가

프로토콜(Protocol) 은 상급 기관이 발급해 아래쪽이 반드시 준수해야 하는 규약입니다. 개별 참여자가 협의해서 만드는 것이 아니라, 위에서 정해져 내려오는 것이라는 점이 핵심입니다.

영화 《미션 임파서블: 고스트 프로토콜》을 떠올리면 감각이 잡힙니다. "고스트 프로토콜 발동"은 개인이 정한 방침이 아니라 조직 상부가 발효시킨 규정이고, 요원들은 그 규정 아래에서 움직입니다.

  • HTTP(HyperText Transfer Protocol) — 웹에서 문서를 주고받는 방식을 정한 규약
  • HTTPS — 거기에 암호화(TLS)를 얹은 것
  • SMTP / FTP / TCP / IP — 각각 메일·파일·전송·주소지정을 담당하는 규약

⚠️ 프로토콜과 아키텍처를 혼동하지 마세요. HTTP는 프로토콜(전송 방식의 규약)이고, REST는 아키텍처 설계 규칙(그 프로토콜을 어떻게 쓸 것인가의 원칙)입니다. 층이 다릅니다.

5.2 GET과 POST — 이 주의 주인공

웹 브라우저(프론트엔드)와 서버(백엔드)가 데이터를 교환할 때 쓰는 HTTP 메서드 중 가장 많이 쓰는 둘입니다.

[ Frontend: f(x) ]  ─── GET (데이터 내려주세요) ───▶  [ Backend (Python Object) ]
                    ◀── 정제된 데이터(JSON) 응답 ───

[ Frontend: f(x) ]  ─── POST (이 이벤트 처리해줘) ──▶  [ Backend (Python Object) ]
                        (Raw Data를 몸체에 실어서 보냄)

🔹 GET (조회 / Read)

  • 기능적 정의: 서버에 존재하는 기존 데이터를 안전하게 '가져와서 화면에 출력할 때' 씁니다.
  • 아키텍처 관점: 서버의 상태나 저장된 데이터를 변경하지 않는 순수한 조회 요청입니다.
  • 특징: 데이터가 URL 주소창에 파라미터 형태로 노출됩니다.
  • 시스템 매칭: 서비스 진입 시 초기 화면 구성 데이터를 불러오는 루틴(GET /).

🔸 POST (생성 및 처리 / Create)

  • 기능적 정의: 사용자가 화면에서 입력한 'Raw Data를 서버로 밀어 넣거나, 특정 이벤트·연산을 발생시킬 때' 씁니다.
  • 아키텍처 관점: 전달할 데이터(이메일, 패스워드, 챗봇 발화 내용 등)를 HTTP 내부 Body(본문)에 담아 전송합니다. 서버 내부에 새 데이터 객체를 만들거나 AI 모델 연산을 기동하는 방아쇠 역할입니다.
  • 시스템 매칭: 사용자가 입력한 메시지를 AI 컴포넌트로 보내는 루틴(POST /chat).

정리표

GETPOST
목적조회 (Read)생성·처리 (Create)
데이터 위치URL 파라미터 — 노출됨Body(본문) — 숨겨짐
서버 데이터변경하지 않음변경함
길이 제한있음(URL 길이)사실상 없음
캐시·북마크가능불가
예GET /posts/15POST /users

⚠️ "POST는 안전하다"는 말을 오해하지 마세요. Body에 담기면 주소창에 안 보일 뿐, 암호화되는 것이 아닙니다. 실제 암호화는 HTTPS가 합니다. 비밀번호를 POST로 보내는 이유는 "숨겨지니까"가 아니라 "URL·브라우저 기록·서버 로그에 남지 않으니까" 입니다.

5.3 Safe / Unsafe 메서드 — 판별 기준은 하나

HTTP 메서드를 가르는 기준은 딱 하나입니다.

"서버의 원본 데이터가 변경되는가?"

분류메서드서버 상태설명인스타그램으로 치면
Safe MethodsGET변화 없음리소스 조회·획득. 요청 전후 서버 DB가 동일피드 구경하기, 스토리 읽기
Unsafe MethodsPOST변화 있음새 데이터를 추가(생성)새 게시물 포스팅
PUT변화 있음기존 데이터를 다른 내용으로 전체 교체(수정)내 게시글 내용 고치기
PATCH변화 있음기존 데이터의 일부만 수정게시글 제목만 고치기
DELETE변화 있음데이터 삭제내가 올린 포스팅 지우기

5.4 멱등성 (Idempotency) — Safe와 헷갈리는 다른 축

Safe와 자주 혼동되지만 다른 개념입니다.

멱등성 — 같은 요청을 여러 번 보내도 결과(서버의 최종 상태)가 한 번 보낸 것과 같은 성질.

메서드Safe (원본을 안 바꾸는가)멱등 (여러 번 해도 같은가)
GET✅✅
PUT❌✅ (같은 값으로 계속 덮어씀)
DELETE❌✅ (이미 지워졌으면 그대로)
POST❌❌ (누를 때마다 새로 생김)

⚠️ POST가 멱등하지 않다는 사실이 실무에서 사고를 만듭니다. 결제 버튼을 두 번 누르면 결제가 두 번 됩니다. 그래서 3주차에서 배운 로딩 중 버튼 비활성화가 단순한 UI 예의가 아니라 데이터 무결성 장치인 것입니다.

5.5 REST API란 무엇인가

REST API 는 HTTP 메서드(GET, POST 등)와 자원의 위치(주소, URI)를 결합하여, 요청의 목적을 직관적으로 알 수 있게 만든 API 설계 규칙입니다.

  • 기본 구조: 주소(URI) + 명령어(HTTP Method) + 내용물(Data)
  • 직관적인 예
    • GET /posts/15 → "15번 게시글을 가져와서 보여줘"
    • DELETE /posts/15 → "15번 게시글을 삭제해줘"

같은 주소에 메서드만 바꾸면 다른 일이 됩니다. 주소는 '무엇'이고, 메서드가 '어떻게'입니다.

5.6 REST API의 3대 구성요소

구성요소영문무엇인가원칙
자원Resource데이터의 위치를 나타내는 주소(URI)오직 명사 형태로 구성
행위Verb자원을 어떻게 다룰지 결정하는 메서드주소 대신 메서드로 기능을 구분
표현Representation실제 주고받는 데이터(JSON 등)리소스의 구체적인 내용물

이 셋을 질문 형태로 바꾸면 주소(무엇을?) + 방식(어떻게?) + 응답(결과는?) 입니다.

AI에게 API 설계를 요청할 때 쓰는 템플릿:

[기능 이름]을 위한 REST API를 설계해줘.
주소, 요청 방식, 필요한 값, 응답 예시, 실패 상황을 함께 정리해줘.

5.7 RESTful의 3가지 조건

REST의 제약 조건들을 엄격하고 올바르게 지킨 시스템을 'RESTful하다'고 부릅니다.

  1. 명사 중심의 주소 (URI) 주소에 get_user, delete_post 같은 동사(행위)를 쓰지 않고, 오직 자원명(users, posts)만 씁니다. 행위는 HTTP 메서드로 대체합니다.
    • ❌ POST /deletePost?id=15
    • ✅ DELETE /posts/15
  2. 무상태성 (Stateless) 서버가 클라이언트의 이전 상태를 기억하지 않습니다. 따라서 클라이언트는 매 요청마다 필요한 모든 정보(토큰 등)를 새로 담아 보냅니다. 서버 확장성에 유리합니다.
  3. 자체 문서화 (Self-descriptive) 요청·응답 메시지 자체만 보아도 데이터 형식(Content-Type: application/json 등)을 추가 설명 없이 해석할 수 있어야 합니다.

5.8 ⭐ RESTful ⊃ Stateless — 포함 관계를 정확히

여기가 이 주에 특히 강조된 지점입니다.

RESTful API는 아키텍처 설계 규칙이고, 무상태(Stateless)는 그 규칙 중 하나입니다.

RESTful API (아키텍처 설계 규칙)
├── 자원을 URL로 표현
├── HTTP 메서드로 제어
├── 클라이언트-서버 분리
├── 캐시 가능(Cacheable)
├── 계층화(Layered System)
└── ★ 무상태 (Stateless)   ← 여럿 중 하나
  • RESTful: 자원을 URL로 표현하고, HTTP 메서드로 제어하며, 클라이언트-서버·캐시·계층화 제약을 지키는 설계 규칙 전체
  • Stateless: 그 규칙 중 하나. 서버가 이전 요청·세션 상태를 기억하지 않고, 클라이언트가 매 요청에 인증 토큰·데이터를 모두 담아 보내는 것

❌ "REST = 무상태"라고 외우면, REST의 나머지 다섯 제약을 영영 못 배웁니다. ⭕ "무상태는 REST의 여섯 제약 중 하나"라고 외우세요.

5.9 CRUD · REST · SQL — 같은 네 동작의 세 이름

앞에서 배운 CRUD 표를 이 주의 문맥에서 다시 봅니다.

동작뜻REST APISQL서버 상태
Create생성POSTINSERT변함 (Unsafe)
Read조회GETSELECT안 변함 (Safe)
Update수정PUT / PATCHUPDATE변함 (Unsafe)
Delete삭제DELETEDELETE변함 (Unsafe)

CRUD는 무엇을 할지, REST API는 어떻게 요청할지, SQL은 DB에서 실제로 실행하는 방법입니다.

이 표가 1절의 파이프라인과 겹칩니다. 같은 "생성" 하나가 프론트에서는 POST, DB에서는 INSERT로 이름을 바꿔 가며 흘러갑니다.

5.10 HTTP 상태코드 — 앞자리만 알아도 절반

응답이 돌아왔을 때 어디부터 봐야 할지를 정해 주는 숫자입니다.

앞자리의미비유
2xx성공주문 완료
3xx이동다른 창구 안내
4xx요청 쪽 문제내 주문서 확인
5xx서버 쪽 문제주방 문제 발생

자주 만나는 것들:

코드의미어디부터 볼까
200 OK성공—
201 Created새로 만들기 성공—
204 No Content성공했지만 돌려줄 내용 없음—
400 Bad Request요청 형식이 이상함입력값·요청 형식·필수값
401 Unauthorized로그인 필요로그인·토큰
403 Forbidden로그인은 했지만 권한 없음권한 설정
404 Not Found주소·자료 없음주소·라우팅·API 경로
405 Method Not AllowedGET 자리에 POST를 보냄메서드
409 Conflict이미 가입된 이메일 등 충돌중복 검사
422형식은 맞지만 내용 검증 실패검증 규칙
429 Too Many Requests횟수 제한 초과호출 빈도
500서버 내부 오류서버 코드·DB 연결·예외 로그
503점검·과부하(3주차에서 배운 대로, 환경변수 미로드일 수도)

401 vs 403 — 401은 "너 누구야?", 403은 "누군지는 알지만 여기 못 들어와". 400 vs 422 — 400은 요청 모양 자체가 이상, 422는 모양은 맞는데 내용이 규칙 위반.

막혔을 때 AI에게 물어보는 템플릿:

제가 [행동]을 했는데, [주소]로 [방식] 요청을 보냈고, 상태코드 [숫자]가 나왔어요.
기대한 결과는 [기대값]인데, 실제 결과는 [현재 상황]이에요. 어디부터 확인해야 할까요?

6. 텍스트가 객체가 되는 두 가지 길 — DOM과 JSON

6.1 Notation vs Annotation — 본문인가 포스트잇인가

발음이 비슷해서 계속 섞이는 두 단어입니다. 역할과 목적이 다릅니다.

Notation (표기법)Annotation (주석 / 라벨)
무엇인가데이터를 기록하는 기본 규칙과 양식 자체기본 데이터 위에 부가 정보·설명·처리 지시어를 추가하는 행위
없으면해석이 불가능 — 본문 그 자체이므로본문의 형태는 유지됨 — 이해를 돕는 보조일 뿐
비유악보, 수식 표기법악보 위에 연필로 적은 메모, 포스트잇
예JSON, 수식(+ − × ÷), 음악 악보파이썬 타입 힌트(age: int), 자바 @Component, AI 학습용 데이터 라벨링
대상본문 데이터(Body Data)메타데이터(Metadata)
  • 공통점: 둘 다 데이터와 관련이 있습니다.
  • 차이점: 역할과 목적이 다릅니다. Notation은 없으면 못 읽고, Annotation은 없어도 읽힙니다.

6.2 JSON — 자바스크립트의 문법이 표준이 된 것

JSON (JavaScript Object Notation) 은 이름 그대로 자바스크립트 객체 표기법입니다.

  • 개념: 자바스크립트에서 객체(Object)를 표현하던 직관적이고 편리한 문법 규칙(Notation) 을 그대로 가져와, 독립적인 데이터 기록·전송 양식으로 표준화한 텍스트 형식입니다.
  • 문법: Key : Value
  • 특징
    • 텍스트 기반이므로 가볍고 프로그래밍 언어에 독립적입니다 (Python, Java, Go 등 모두 지원)
    • 반드시 쌍따옴표(") 를 써야 하는 엄격한 규칙이 있습니다
{
  "userId": "a",
  "nickname": "a",
  "email": "a@b",
  "role": "admin"
}

⚠️ 가장 흔한 JSON 에러가 따옴표와 쉼표입니다. 홑따옴표(')를 쓰거나 마지막 항목 뒤에 쉼표를 남기면 파싱이 실패합니다. 자바스크립트 객체 리터럴은 관대하지만 JSON은 엄격합니다. 둘은 문법이 비슷할 뿐 같은 것이 아닙니다.

6.3 직렬화와 역직렬화 — 데이터 교환의 왕복

3주차에서 배운 SAVE/LOAD(직렬화/역직렬화)가 여기서 네트워크 버전으로 다시 나옵니다.

[메모리의 객체]
      │
      │ ① 직렬화 (Serialization / Stringify) — 객체 ➡ 텍스트
      ▼
[JSON 텍스트]
      │
      │ ② 네트워크 전송
      ▼
[JSON 텍스트]
      │
      │ ③ 역직렬화 (Deserialization / Parsing) — 텍스트 ➡ 객체
      ▼
[Python Dictionary / Java Object]
  1. 직렬화 (Stringify): 메모리상의 실제 객체를 네트워크로 보내기 위해 JSON 텍스트로 변환
  2. 역직렬화 / 파싱 (Parsing): 받은 JSON 텍스트를 대상 언어가 이해할 수 있는 실제 객체(예: 파이썬 딕셔너리)로 복원
언어직렬화역직렬화
JavaScriptJSON.stringify(obj)JSON.parse(text)
Pythonjson.dumps(obj)json.loads(text)

왜 굳이 텍스트로 바꾸나: 3주차에서 배운 대로 메모리 객체는 입체적입니다. 그대로는 선(네트워크)을 통과할 수 없습니다. 일렬로 늘어뜨려야(直列, serial) 보낼 수 있습니다. 그래서 직렬화입니다.

6.4 DOM — 정적 HTML이 동적 객체 트리가 되는 순간

DOM(Document Object Model) 은 브라우저가 HTML 코드를 읽어 메모리에 생성한 객체 트리입니다. 웹페이지를 구성하는 요소들을 객체지향 관점의 '객체(Object)'로 변환한 것입니다.

[정적 HTML 문서] ──(브라우저 파싱)──▶ [메모리 내 DOM 객체 트리 생성]
                                              │
                    ┌─────────────────────────┴─────────────────────────┐
                    ▼                                                   ▼
       [① 속성: Property / Attribute]                      [② 기능: Method / Script]
       - id, class, style, data-*                          - 이벤트 처리, DOM 동적 제어
       - 데이터의 상태 보관 (변수)                            - 상태를 바꾸는 로직 (함수)

여기서도 축 2(속성 + 기능 = 객체) 가 그대로 반복됩니다. DOM은 객체이므로 둘 다 가집니다.

① 속성 (Property / Attribute)

  • 개념: 웹페이지 구성 요소(태그)가 가진 '상태' 나 '데이터'.
  • 예
    <body id="tt-body-page" data-theme="light">
    <div id="__loader" class="fade-out-box" style="display: none;">
    
  • 역할: 고유 식별자(id), 디자인 스타일(class, style), 사용자 정의 데이터(data-*)를 담는 변수 역할.

② 기능 (Method / Function)

  • 개념: 웹페이지를 동적으로 움직이게 하고 제어하는 '로직' 이나 '행동'.
  • 예: <script> 태그 내부의 자바스크립트, 이벤트 리스너. 조건에 따라 경고창을 띄우거나(alert()), 로딩이 끝나면 로딩 화면을 숨기는(display: none) 함수.
  • 역할: 속성값을 실시간으로 변경하거나, 클릭·입력 이벤트를 감지해 처리하는 메서드 역할.
    element.style.display = 'block';
    

객체지향과의 완전 대응

구분DOM (웹 브라우저)일반 객체지향 (파이썬 등)역할
대상HTML 태그들 (<body>, <div>)클래스 인스턴스 (Instance)메모리에 실체화된 독립 객체
속성어트리뷰트 (id, class, style)멤버 변수 (Attribute / Field)객체가 가진 고유 데이터·상태값
기능스크립트 (<script>, Event)메서드 (Method / Function)상태를 바꾸고 연산을 수행하는 로직

이 대응을 이해하면 얻는 것이 두 가지입니다. ① 웹 크롤러(BeautifulSoup, Selenium)로 원하는 데이터를 정확히 추출할 수 있습니다 — 트리 구조를 알기 때문입니다. ② 스크립트로 화면을 자유롭게 제어할 수 있습니다 — 무엇이 속성이고 무엇이 기능인지 알기 때문입니다.

6.5 DOM과 JSON의 본질 — 둘 다 텍스트를 객체로 만드는 일

이 주에서 가장 오래 남는 통찰입니다.

DOM과 JSON 모두, 처음부터 프로그래밍 언어 안의 '순수 메모리 객체'로 존재하는 것이 아닙니다. 텍스트로 존재하던 대상을 특정 목적에 맞게 가공함으로써 비로소 프로그램이 제어할 수 있는 '객체'가 됩니다.

구분DOMJSON
본질 (이전 상태)HTML 문서 (텍스트)데이터 규격 (텍스트)
핵심 행위구조적 모델링 (Modeling)규칙에 따른 표기법 (Notation)
1단계브라우저가 태그 간 부모-자식 관계를 분석해 트리 모델로 설계서로 다른 언어가 통신하도록 "Key": "Value" 형태로 적는 표기 규칙
2단계 (객체화)노드 하나하나를 메모리상의 객체로 변환각 언어의 파서(Parser) 가 해석해 해당 언어의 메모리 객체로 변환
접근 방법document.getElementById()JSON.parse(text)
최종 상태 (이후)브라우저가 제어하는 트리 구조 객체언어별 파서가 해석한 네이티브 메모리 객체
목적화면을 제어하기 위해시스템 간 데이터를 교환하기 위해

⭐ 핵심 한 줄: 둘 다 TEXT → PROGRAMMABLE OBJECT 입니다. 다만 DOM은 구조를 세우는 길(모델링) 로 가고, JSON은 형식을 맞추는 길(노테이션) 로 갑니다.

3주차 1.5절에서 "터미널 속 죽어 있는 글자(개체)에 함수를 부여해 메모리상에 객체로 부활시키는 것이 프로그래밍"이라고 했습니다. DOM과 JSON은 그 문장의 가장 구체적인 두 사례입니다.


7. UI = f(state) — 리액트의 구조

7.1 공식 하나로 요약되는 프레임워크

리액트 컴포넌트의 본질은 수학 공식 하나로 요약됩니다.

UI = f(state)

"상태를 입력받아 화면을 그려내는 함수"

State  ───▶  f()  ───▶  UI
(상태)      (컴포넌트)   (화면)

이것이 1절에서 프론트엔드를 함수라고 부른 이유입니다. 리액트 컴포넌트는 말 그대로 함수입니다.

7.2 웹 동작 구조 — 세 조각의 위치

조각어디에 있나성격
State (상태)JavaScript / React 영역화면에 동적으로 반영되어야 하는 '살아있는 데이터'
HTML함수의 결과물React 함수가 실행되어 뱉어내는 '정적인 결과물(UI 껍데기)'. 자체적으로 상태를 저장하지 않는 Stateless 속성
JSON / Request시스템 외부(서버)와의 경계내부 State(객체)를 JSON 표기법으로 직렬화하여 전송

⚠️ HTML이 Stateless라는 말은 "HTML 안에 상태가 없다"는 뜻입니다. 4.2절의 통신 무상태와는 다른 층위의 말입니다. 같은 단어가 층위마다 다른 대상을 가리키므로, "무엇이 무엇을 기억하지 않는가" 를 매번 확인하세요.

7.3 useState 훅 — 읽기와 쓰기를 분리한다

리액트는 안전한 상태 관리를 위해 읽기 권한과 쓰기 권한을 엄격히 분리합니다.

const [state, setState] = useState<SignupPageState>(initialSignupState)

한 줄에 네 가지가 들어 있습니다.

부분이름역할
state읽기 전용 (Get)현재 상태값. 직접 바꾸면 안 됩니다
setState변경 함수 (Set)이 함수로만 상태를 바꿉니다. 호출하면 재렌더링(Re-rendering) 이 일어납니다
<SignupPageState>타입스크립트 규칙이 상태가 어떤 모양이어야 하는지
initialSignupState초기값처음 화면이 그려질 때의 값

⭐ 왜 state = 새값 으로 직접 바꾸면 안 되는가 리액트는 setState 호출을 신호로 삼아 "이제 화면을 다시 그려야겠다"를 판단합니다. 직접 바꾸면 값은 바뀌지만 화면이 안 바뀝니다. "값은 맞는데 화면이 그대로"인 버그의 90%가 여기서 나옵니다.

7.4 컴포넌트 · 함수 · 화살표 함수 · 핸들러

회원가입 화면 코드 하나에 이 네 가지가 전부 들어 있습니다.

개념정의코드 예
컴포넌트 (Component)UI를 구성하는 독립적이고 재사용 가능한 최소 단위 (레고 블록)SignupPage 전체
펑션 (Function)특정 작업을 하도록 설계된 독립적인 코드 블록export default function SignupPage() { ... }
화살표 함수 (Arrow Function)ES6에 도입된 더 간결한 함수 문법 (=>)const handleGoogleSignIn = async () => { ... }
핸들러 (Handler)사용자의 행동(이벤트)이 발생했을 때 실행되는 함수handleGoogleSignIn, handleSubmit

⚠️ 리액트 컴포넌트의 이름은 반드시 대문자로 시작해야 합니다. 소문자로 시작하면 리액트가 그것을 HTML 태그로 오해합니다. <signupPage /> 는 존재하지 않는 HTML 태그가 되어 버립니다.

7.5 ⭐ e.preventDefault() — 오해 바로잡기

이 함수는 수업에서 가장 많이 오해받은 코드입니다.

  • 단어 뜻: prevent(막다) + Default(기본 행동) = "브라우저가 기본적으로 실행하는 행동을 하지 못하게 막아라"
  • 막는 대상: HTML <form> 태그가 가진 기본 성질인 '제출(Submit) 시 페이지 전체를 새로고침(Refresh)하는 행동'

왜 리액트에서 새로고침을 막아야 하나

리액트 앱에서 화면 전체가 새로고침되면, 화면이 깜빡이면서 컴포넌트가 들고 있던 상태(State)와 사용자가 입력 중이던 데이터가 순간적으로 모두 초기화(증발) 됩니다. 7.1절의 공식 UI = f(state) 에서 state가 사라지면 UI도 사라집니다.

있을 때와 없을 때

구분e.preventDefault() 없을 때e.preventDefault() 있을 때
작동 주체HTML / 브라우저 자체 기능자바스크립트 (개발자가 짠 스크립트)
화면 상태흰 화면이 잠깐 나오며 페이지 전체 새로고침새로고침 없음 (현재 화면 상태 그대로 유지)
전송 방식HTML 폼 기능이 주도해 무조건 백엔드로 전송 시도HTML의 자동 전송은 차단됨. 이후 작성한 JS 코드(fetch, axios)로 전송
특징고전적인 웹 방식 (느리고 깜빡임)현대적인 리액트 방식 (빠르고 부드러움)

💡 오해 바로잡기

  • ❌ 틀린 이해: e.preventDefault() 가 있어야 백엔드로 전송이 된다.
  • ⭕ 바른 이해: e.preventDefault() 자체는 데이터를 백엔드로 보내는 기능이 아닙니다. HTML의 무식한 새로고침을 방해하는 방패일 뿐입니다. 화면을 그대로 유지시켜서, 그 아래에 개발자가 적어 둔 진짜 백엔드 전송 코드(fetch, axios)가 도중에 끊기지 않고 안전하게 실행되도록 자리를 깔아주는 역할을 합니다.

즉, 전송은 axios 가 하고, preventDefault 는 그 사이에 브라우저가 끼어들지 못하게 막습니다. 둘의 역할을 섞으면 "왜 데이터가 안 가지?"의 원인을 영영 못 찾습니다.

7.6 단방향 흐름 — 화면이 먼저 바뀌지 않는다

리액트가 전제하는 흐름은 한 방향입니다. 수도관에서 물이 한 방향으로 흐르는 것과 같습니다.

사용자 클릭 → 이벤트 발생 → 상태 변경 → 화면 다시 그리기 → 사용자 확인

⭐ 화면이 먼저 바뀌는 게 아니라, 데이터(상태)가 먼저 바뀌고 화면이 따라옵니다.

이 순서를 뒤집어 생각하면(“화면을 직접 바꾸자”) 곧바로 DOM을 직접 만지게 되고, 리액트를 쓰는 의미가 사라집니다. DOM을 직접 제어하는 것은 6.4절의 고전적 방식이고, 리액트는 상태만 바꾸면 DOM은 알아서 맞춰지는 방식입니다.


8. 상태를 가지는 함수 — 클로저와 람다

4.6절에서 "함수는 무상태"라고 배웠습니다. 이제 그 예외를 봅니다. 이 예외를 이해하는 것이 이 장에서 가장 어렵고, 가장 값진 부분입니다.

8.1 세 개의 이름 — 실행 영역 · 감싸진 영역 · 클로저

이름정의
실행 영역 (바깥쪽 함수 / Outer Scope)함수가 호출되어 실행될 때 생성되는 공간. 그 함수 내부의 변수와 매개변수가 메모리에 할당되어 존재하는 유효 범위(Scope)
감싸진 영역 (안쪽 함수 / Inner Scope)바깥쪽 함수 내부에서 정의되거나 반환되는 함수, 또는 특정 블록(JSX 등). 자체 유효 범위를 가지면서도 선언된 위치의 바깥 환경에 접근할 권한을 가짐
클로저 (Closure)안쪽 함수가 외부 함수의 실행이 종료된 후에도 바깥쪽 함수의 변수 공간(Lexical Environment)을 기억하고 계속 접근할 수 있는 현상 또는 구조

그림으로 그리면 이렇습니다.

┌──────────────────────────────────────────────┐
│  함수 스코프 (Outer Function Scope)           │
│  ┌────────────────────────────────────────┐  │
│  │ 상태 변수 (예: x = 10)                  │  │ ◀── 클로저(Closure)에 의해
│  └────────────────────────────────────────┘  │     외부 함수 종료 후에도
│  ┌────────────────────────────────────────┐  │     메모리 공간 [유지]
│  │ 람다 함수 (익명 내부 함수)               │  │
│  │ lambda y: x + y                        │  │
│  └────────────────────────────────────────┘  │
└──────────────────────────────────────────────┘
  • 가장 바깥 네모 = 함수 / 스코프: 외부 함수가 호출될 때 생성되는 렉시컬 환경(Lexical Environment). 실행 시점에 고유한 메모리 블록(심볼 테이블)이 할당되고, 지역 변수와 매개변수가 여기 배치됩니다.
  • 안쪽 작은 네모 = 람다: 외부 함수 내부에서 동적으로 정의되는 익명 함수(Anonymous Function). 실행되기 전까지 하나의 '코드 블록' 혹은 '객체'로 존재하며, 상위 스코프의 변수를 참조(Reference) 하는 논리 구조를 가집니다.
  • 왼쪽의 괄호 선 = 클로저: 내부 함수(람다)가 자신을 둘러싼 외부 스코프를 참조 구조 채로 묶어서 들고 나오는 현상 그 자체.

8.2 무상태여야 할 것이 상태를 갖게 되는 순간

본래 함수는 실행 완료(return)와 동시에 스코프가 파괴되는 무상태(Stateless) 특성을 가집니다. 그러나 클로저가 형성되면 외부 함수가 끝났음에도 스코프 내의 데이터가 파괴되지 않고 메모리에 유지(Stateful) 됩니다.

NORMAL BEHAVIOR (STATELESS)         │        CLOSURE BEHAVIOR (STATEFUL)
────────────────────────────────────┼────────────────────────────────────
 함수 실행                           │  ❶ 외부 함수 실행
   ↓                                 │       람다와 지역 변수 정의 (counter=10)
 변수 잠깐 접근                       │  ❷ 클로저 생성
   ↓                                 │       람다 객체가 외부 스코프 변수를 감쌈
 STACK FRAME                        │       CLOSURE OBJECT → HEAP 으로 승격
   ↓                                 │  ❸ 상태 유지 (PERSISTENT STATE)
 ❌ 소멸                             │  ❹ 이후 호출: 11 → 12 로 누적됨
 GC가 수거                           │       REFERENCE COUNT > 0 → GC 무시 ✅

8.3 메모리에서 실제로 일어나는 일 — 스택에서 힙으로

왜 원래 사라져야 할 스코프가 유지될 수 있을까요? 세 단계로 설명됩니다.

① 스택(Stack)과 힙(Heap)의 반전 일반적인 함수의 지역 변수는 스택 프레임에 할당되어 함수 종료 시 자동 소멸합니다. 하지만 클로저에 의해 참조되는 변수는 힙(Heap) 메모리 영역으로 승격되어 관리됩니다.

  • 자바스크립트: 렉시컬 환경 객체
  • 파이썬: __closure__ 셀(cell) 객체

② 참조 횟수 (Reference Counting) 외부 함수가 종료되더라도, 반환된 내부 람다 함수 객체의 내부 속성(__closure__)이 외부 함수의 변수 공간을 여전히 참조하고 있습니다.

③ 생명주기 연장 참조 횟수가 0이 되지 않기 때문에, 가비지 컬렉터(GC)가 그 메모리 블록을 회수하지 않습니다. 그 결과 함수 지향 프로그래밍에서도 안전하게 '상태를 가지는 함수(Stateful Function)' 를 구현할 수 있게 됩니다.

앞에서 배운 메모리 3영역이 여기서 정확히 쓰입니다. Stack은 메서드가 끝나면 즉시 사라지고, Heap은 참조가 끊길 때만 GC가 수거합니다. 클로저는 그 "참조가 끊기지 않게 하는" 장치입니다. 3주차 2.6절 표를 다시 펼쳐 보세요.

8.4 파이썬 표준 코드로 보는 메커니즘

def outer_function(x):
    # [가장 바깥 네모]: 외부 함수 스코프 생성
    # 변수 'x'가 이 스코프 메모리에 할당됩니다.

    # [안쪽 작은 네모]: 람다 함수 정의 및 반환
    # 이 람다가 상위 스코프의 'x'를 참조하면서 [클로저]가 형성됩니다.
    closure_lambda = lambda y: x + y

    return closure_lambda

# 1. 외부 함수를 실행하여 클로저 인스턴스를 생성합니다.
#    이 순간 outer_function(10)의 실행은 완전히 종료됩니다.
my_closure_10 = outer_function(10)
my_closure_20 = outer_function(20)

# 2. 외부 함수가 종료되었음에도 내부의 'x' 상태는 독립적으로 [유지]됩니다.
print(my_closure_10(5))  # 출력: 15  (10 + 5)
print(my_closure_20(5))  # 출력: 25  (20 + 5)

⭐ my_closure_10 과 my_closure_20 이 서로 다른 x 를 기억한다는 것이 핵심입니다. 같은 코드에서 나온 두 함수가 각자의 상태를 가지고 있습니다. 이건 이미 객체와 거의 같은 성질입니다.

8.5 JSX가 클로저인 이유 — 리액트 코드가 갑자기 읽힌다

리액트 컴포넌트를 보면, JSX 블록 안에서 상위에 정의된 변수를 아무렇지 않게 씁니다.

// 컴포넌트 함수 내부 (외부 실행 환경)
const { email, password, name, role, agreed } = useSomeState();

return (
  // ========================================================
  // [감싸진 영역] 전체 UI 렌더링을 위한 JSX 블록 시작
  // ========================================================
  <div className="flex min-h-screen bg-background">
    <div className="flex flex-col justify-center flex-1 px-4 py-12">
      <div className="w-full max-w-sm mx-auto">

        {/* JSX 블록 내부(감싸진 영역)에서 상위 컴포넌트 영역에 정의된
            'email', 'password' 등의 변수를 그대로 가져와 참조할 수 있는 것은
            자바스크립트의 Lexical Scope와 클로저 규칙에 의해
            안쪽 영역이 바깥쪽 영역의 변수 환경을 감싸고 유지하기 때문입니다. */}
        <span>{email}</span>

      </div>
    </div>
  </div>
);

같은 원리가 배열 처리에서도 나옵니다.

function formatSignupApiError(detail: unknown): string {
  // [실행 영역] 바깥쪽 함수 범위 시작 — 매개변수 'detail'이 활성 상태

  if (Array.isArray(detail)) {
    return detail
      .map((item) => {
        // [감싸진 영역] 안쪽 화살표 함수 — 외부의 'detail' 환경을 기억 (클로저)
        return typeof item === "object" && item !== null && "msg" in item
          ? String((item as { msg: string }).msg)
          : JSON.stringify(item);
      })
      .join(", ");
  }

  return "회원가입 요청에 실패했습니다.";
  // [실행 영역] 바깥쪽 함수 범위 끝
}

8.6 스코프 체인 — 변수를 찾아 올라가는 길

  1. 스코프 체인(Scope Chain): 안쪽 영역에서 변수를 찾을 때, 내부에 없으면 자신을 감싸고 있는 바로 바깥쪽 실행 영역으로 올라가 탐색합니다.
  2. 참조 유지: 안쪽 영역이 존재하는 한, 바깥쪽 실행 영역의 메모리 자원은 소멸하지 않고 링크가 유지됩니다. 이 연결 고리 전체가 클로저 메커니즘입니다.

앞으로 다시 만납니다 이 "메모리 주머니 모델"을 머릿속에 넣어 두면, 앞으로 만날 데코레이터(Decorator), 커링(Currying), 비동기 콜백(Callback) 패턴이 전부 같은 그림으로 읽힙니다. 셋 다 "함수를 반환하면서 바깥 환경을 들고 나오는" 구조입니다.