[2026] RTMP(Real-Time Messaging Protocol) 완전 레퍼런스 — 핸드셰이크·청크·Nginx·FFmpeg·OBS·Node
이 글의 핵심
RTMP의 바이트 단위 핸드셰이크·청크 스트림·명령(AMF0)·오디오/비디오 메시지를 스펙 관점에서 정리하고, Nginx-RTMP·FFmpeg·OBS·Node(node-media-server)로 송수신·인증·멀티비트레이트까지 이어집니다. RTMPS/RTMPT·버퍼·지연·방화벽·성능 튜닝은 운영 체크리스트로 수렴합니다.
RTMP(Real-Time Messaging Protocol)는 원래 플래시(Flash) 미디어 서버와 플레이어 간에 정의되었고, 이후 송출(ingest) 쪽에서는 여전히 사실상 표준에 가깝게 쓰입니다. TCP 한 개의 스트림 위에서 핸드셰이크 → 청크 스트림 다중화 → AMF(주로 AMF0) 명령으로 채널을 열고, 그 위에 오디오·비디오 프레임이 실립니다. 이 글은 “버튼만”이 아니라 패킷이 어떻게 쌓이는지를 알면 장애가 반으로 줄어든다는 전제로, 스펙·실무·고급·트러블슈팅을 한 덩어리로 정리합니다.
전제: RTMP 재생(배포) 는 브라우저 네이티브가 사실상 사라졌기 때문에(플래시 제거) HLS·DASH·LL-HLS 등 HTTP 배포와 RTMP ingest 를 접목하는 그림이 일반적입니다. 그래서 이 글도 ingest+전송(미디어 서버) 중심으로 서술합니다.
RTMP가 TCP에서 하는 일 (큰 그림)
RTMP는 전송 자체를 UDP처럼 “손실 허용”으로 두지 않고, TCP의 신뢰성(순서·재전송) 에 기대 바이트 스트림을 말합니다. 그래서 지터·늦은 ACK·재전송이 쌩으로 체감 지연으로 이어질 수 있고, 이는 “프로토콜이 나빠서”라기보다 전송 제어(혼잡·버퍼) 가 어느 층에 있느냐의 문제이기도 합니다. (TCP 혼잡·버퍼에 대한 틀은 TCP 가이드를 함께 보면 이해가 빨라집니다.)
3-way handshake는 OS가, RTMP 핸드셰이크는 앱이
클라이언트·서버가 connect()/accept()로 TCP 3-way handshake 를 마친 뒤, RTMP는 추가로 앱 레벨 핸드셰이크 를 합니다. 이 단계는 OS 소켓 API가 자동으로 해주는 게 아니라, RTMP 엔드포인트(nginx-rtmp, node-media-server 등)가 바이트를 읽고 씁니다.
RTMP 앱 수준 핸드셰이크(요지)
전형적인 RTMP(버전 3) 는 C0/C1, S0/S1, C2, S2 를 주고받는 흐름이 널리 알려진 스키마(레거시 문서·구현)입니다.
| 단계 | 보내는 측 | 내용(요지) |
|---|---|---|
| 1a | Client → Server | C0: 1바이트 버전(보통 0x03) |
| 1b | Client → Server | C1: 1536바이트(타임/랜덤·구현에 따라 4+4+152 등 필드) |
| 2a | Server → Client | S0/S1 |
| 2b | Server → Client | S2: 클라이언트 C1에 대한 에코(검증) |
| 3 | Client → Server | C2: 서버 S1에 대한 에코(검증) |
- 핵심 의도: (1) 프로토콜 버전 합의, (2) 무작위성으로 중간에 끼어드는(Replay) 낚시를 억제(구체 필드·정확한 바이트 배치는 “구현/문서/캡처”를 함께 봐야 합니다. 서버/클라이언트마다 타임필드 해석/옵션이 약간씩 갈릴 수 있습니다).
- 운영 관점:
tcpdump/wireshark에서 1935 포트를 봤을 때, “TCP 연결은 됐는데 2~3초 뒤 끊김” 이면, 먼저 RTMP 핸드셰이크 실패(버전, 프록시가 첫 바이트를 건드림, SNI 없는 TLS 시도, 잘못된 포트로 HTTPS 붙이기) 를 의심합니다.
청크(Chunk)와 메시지(메시지 포맷)
RTMP는 한 TCP 연결에 여러 논리 채널이 동시에 섞이도록, 데이터를 청크(Chunk) 로 잘랐다가, 앞에 Chunk Header(가변 길이) 를 붙입니다. 청크는 “전송 효율(헤더 중복을 줄이기 위한 포맷)” 을 겨냥합니다.
-
기본 흐름(개념)
- 상위 “논리 메시지(Logical Message)”가 타입·길이·스트림 id·timestamp 를 갖는다.
- 이 메시지를 청크 사이즈(예: 128바이트 기본, 협상 가능) 단위로 잘라 여러 chunk 로 흩뿌린다.
- 각 chunk 앞 Chunk Header 는 fmt(포맷 타입), CSID(Chunk Stream ID), (필요시) message stream id/timestamp 확장이 붙는다(구체 레이아웃은 1~3 바이트 CSID, fmt 0/1/2/3 등 “중복을 줄이는 프레이밍”이 핵심).
-
CSID(Chunk Stream ID): “이 청크는 어느 논리 채널의 연속이냐”를 키로 삼습니다(구현/문서에 따라 2~3바이트로 확장).
-
message stream id:
NetConnection/NetStream등 AMFcreateStream뒤에 나오는 정수 id 쪽(매핑을 엔드포인트가 유지)과 연동되는 개념이 실전에서 가장 흔히 튀어나옵니다. -
timestamp(및 delta): 동기(오디오/비디오 정렬), 재생 속도(일부 케이스), B 프레임/오디오 전략과 팀을 이루며, “서버는 송출자의 타임베이스를 신뢰/조정(특히 re-mux)할지” 정책이 생깁니다.
실무 TIP: “프레이밍(청크) 이해 + Wireshark(필터
tcp.port==1935) + 서버 로그(nginxdebug/access)” 3총사가 가장 싸고 빠른 디버깅 루트입니다.
메시지 타입(요지)
문서/구현에서 자주 쓰는 Message Type ID (예, 상위 1바이트 분류)는 대략 다음 범주로 이해할 수 있습니다(세부는 서버/클라이언트가 일부를 생략·축약).
| Type (예시) | 설명(실무) |
|---|---|
| 1(Chunk Size), 2(Abort) 등 | 세션·프로토콜 제어(청크 사이즈 변경 요청, 메시지 중단 등) |
| 3(Ack), 5(Window Ack Size), 6(Set Peer Bandwidth) | 흐름/인지(ack)·윈도·대역 관련 제어(구현/호환에 따라 쓰임이 다름) |
| 8(Audio), 9(Video) | AV 페이로드(codec 바이트 + 프레임) — 가장 “대역”을 먹는 부분 |
| 20(Command: AMF0), (레거시/확장) | connect/play/publish/createStream/deleteStream/FCunpublish 등 |
| (Flex 메시징) | invoke·notify 계열(구버전·Flex 스택) |
- “왜 AMFs가 튀는가”: RTMP 세션/제어 는 AMF(주로 0) 직렬화 로 JSON처럼 생긴 오브젝트를 씁니다. 그래서 퍼블리시/플레이/디스커넥 의 초기 단계에 “명령(Invoke)”/“Data(@setDataFrame)”—메타(코덱, 해상도, 키프레임 정보 등) 를 곁들입니다.
스트림 ID·타임스탬프 운용(엔지니어 체크리스트)
- message stream id:
createStream이후publish/재생 쪽으로 이어질 때, “어느 NetStream(논리 스트림)인가” 를 가리킵니다(멀티 스트림/푸시/리퍼브 시 매핑이 꼬이면 404에 가까운 이상이 납니다). - timestamps(absolute vs delta): 오디오/비디오 동기( lipsync ) 와, A/V 인터리브(교차) 설계에 직접적입니다. GOP(키프레임) 간격·B-frame·VFR 이 합쳐지면 “플레이어는 괜찮은데 HLS 쪽만 끊기는” 류가 나기도 합니다(그때 트랜스코딩/재뮤싱으로 정리하는 게 안전).
- BWE(Set Peer Bandwidth): “대역”을 신호하는 값이지, TCP 혼잡제어를 강제로 끄는 뜻이 아닙니다(서버/클라 호환/무시가 섞일 수 있음). 실효는 항상 측정이 답.
Nginx-RTMP 모듈(대표) 완전 템플릿
주의(법·보안):
record,HLS공개,exec쉘, 콜백http엔드포인트·시크릿, CORS/도메인 제한 은 반드시 당장 정책과 함께 점검하세요(여기는 기능 중심).
# /etc/nginx/nginx.conf (또는 conf.d/ 아래 rtmp { ... } 블록)
worker_processes auto;
# 데몬/로그는 배포에 맞게
rtmp {
server {
listen 1935;
# chunk_size: 청크 전송(미디어) 단위 — 너무 작으면 오버헤드, 너무 크면 지연(체감)↑
chunk_size 4096; # 128~4K+ 범위에서 샘플링(환경·코덱·CPU에 따라)
# buflen: 내부 임시 버퍼(구현/버전/문서 스코프: 운영자가 "어느 지점에서 막힌다" 를 캡쳐로 대조)
buflen 1s;
application live {
live on;
# --- 녹화(옵션) ---
# record all;
# record_path /data/rec;
# record_unique on;
# record_suffix .flv;
# --- HLS(배포 쪽 흔한 패턴) ---
hls on;
hls_path /var/www/hls;
hls_fragment 2s; # 지연 vs 안정(세그 짧을수록 지연↓, 스위치 비용/오버헤드↑)
hls_playlist_length 6s;
# 푸시(옵션: CDN/다른 rtmp)
# push rtmp://cdn.example.com/app1;
# rtmp://host/live/$name 로 들어온 퍼블리시/플레이를 콜백으로 사전승인
on_play http://127.0.0.1:9000/auth;
on_publish http://127.0.0.1:9000/auth;
}
}
}
http {
server {
listen 80;
server_name _;
# HLS 정적 서빙(HTTPS는 별도 server 블록/인증서)
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /var/www;
add_header Cache-Control no-cache;
add_header Access-Control-Allow-Origin *; # 잠깐: 실서비스는 엄격히(도메인 제한)
}
}
}
chunk_size: “프로토콜/구현/환경”에 따른 전송/CPU 트레이드오프 (작게=헤더 비중↑, 크게=지연/버퍼 체감↑ 가능). 일률 “이게 정답” 은 없고 A/B(대역, CPU, 지연) 측정이 맞습니다.on_publish/on_play:http콜백으로 2xx=허가,403거절 같은 패턴(구현/버전에 맞는 응답 형식/타임아웃 필수)이 핵심인데, 뒤이어 Node 예시로 붙입니다.
간이 인증(Express 예시): 실서비스는 HMAC, IP 제한, 토큰 만료, 레이트리밋, 감사로그:
// auth-server.js (node 18+)
// npm i express
import express from "express";
const app = express();
// nginx-rtmp의 http 콜백은 x-www-form-urlencoded body가 흔함(버전에 따라 GET query도)
app.post("/auth", express.urlencoded({ extended: true }), (req, res) => {
const name = String(req.body.name || "");
const type = String(req.body.type || ""); // on_publish, on_play 등
const ip = String(req.body.addr || ""); // client ip (필드명은 환경 확인)
const ok = /^\w+-\d+$/.test(name); // 예: live-2026042312 형태만 허용(샘플)
if (ok) return res.status(200).send("on"); // 일부 환경은 본문 "on" 트리거(문서 확인)
return res.status(403).end();
});
app.listen(9000, "127.0.0.1", () => {
console.log("rtmp http auth: http://127.0.0.1:9000/auth");
});
운영 시 Nginx(HTTP) + auth 서버 사이 루프백/타임아웃/최대 본문 과 Nginx( RTMP) → auth 의 필드명이 맞는지(버전별 문서)를 반드시 대조하세요. “403만 찍힘”의 절반은 필드명 불일치입니다.
FFmpeg로 RTMP 퍼블리시(핵심 옵션)
기본(재인코딩):
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -tune zerolatency -g 50 -keyint_min 50 -sc_threshold 0 \
-c:a aac -b:a 128k -ar 44100 -ac 2 \
-f flv "rtmp://rtmp.example.com/live/STREAMKEY"
copy (재인코딩 없이 그대로 밀기 — 컨테이너/코덱이 RTMP+FLV 경로에 적합할 때):
ffmpeg -re -i srt://source:2000?mode=caller \
-c copy -f flv "rtmp://rtmp.example.com/live/KEY"
-re: 입력 속도=실시간(재생률): 파일을 “실시간처럼” 읽는 데 쓰지만, 이미 RTMP/UDP 등 라이브 소스면 불필요/오히려 꼬일 수 있어요. “무엇이 입력이냐”를 먼저 고릅니다.-g/키프레임 간격: HLS+플레이어 “시작/시크(특히 LL-HLS 아닌 경우)”/에러 복원성과 상충(짧을수록 대역/CPU↑). 일률은 없고 콘텐츠+플랫폼 기준.- B-프레임/프로파일/레벨: 일부 구형 단말/브라우저 경로(특히 트랜스코딩 누락) 에서 재생 이슈로 이어질 수 있어, A/B(디코딩/CPU/지연/품질) 측정이 맞습니다.
OBS Studio (송출) — 실제로 누르는 항목
- 설정 → 방송
- 서비스: 사용자 지정
- 서버:
rtmp://HOST/app(끝에/와스키(키/경로)구분) - 스트림 키: 서버/발급 정책(회전, 만료)을 따름(키가 URL이 아닐 때가 많습니다).
- 출력(인코딩):
x264vsNVENC/QuickSync/AMF— CPU/화질/지연의 삼박자. - 키프레임(간격): 보통
2s를 많이 쓰지만(플랫폼 권고 따름) GOP/장면에 따라 달라짐. 너무 긴 GOP 는 끊김 복원·HLS zapping 측면에서 불리. - 고급(네트워크): 동적 비트레이트·B-프레임·CBR/VBR — HLS+ABR 로 갈수록 “단일 CBR”만으로 끝나는 경우는 줄고, 서버/트랜스코딩/ABR 래더 쪽 정책이 커집니다.
주의(보안):
rtmp://user:pass@host크리덴셜 은 캡쳐/로그/히스토리/오픈 PR에 그대로 남는 경우가 잦으니(그리고 공개 배포 쪽에서 지원/권고가 흔치 않아요) 키 기반+HTTPS 콜백(백채널), SRT(옵션), STunnel/RTMPS 등 운영 체계로 가는 팀이 많습니다. (TLS/HTTPS 틀을 같이 읽는 걸 권장합니다.)
Node.js: RTMP 서버(권장 — 잘 다듬어진 모듈)
직접 raw 소켓으로 “전체” RTMP 를 구현하려면 청크 프레이밍·AMF0·A/V demux 까지 한 번에 들어갑니다. 운영에서는 Nginx-RTMP / SRS / OvenMediaEngine / node-media-server 처럼 검증된 구현을 쓰는 것이 비용 대비 가장 합리적입니다. 학습+소규모 로는 node-media-server 가 빠릅니다.
// nms.mjs
// npm i node-media-server
import NodeMediaServer from "node-media-server";
const nms = new NodeMediaServer({
rtmp: {
port: 1935,
chunk_size: 4096,
gop_cache: true,
ping: 30,
ping_timeout: 60,
},
http: {
port: 8000,
mediaroot: "./media",
allow_origin: "*", // dev 전용(실서비스는 CORS/도메인 제한)
},
trans: {
ffmpeg: process.platform === "win32" ? "ffmpeg.exe" : "/usr/bin/ffmpeg",
tasks: [
{
app: "live",
hls: true,
hlsFlags: "[hls_time=2:hls_list_size=3:hls_flags=delete_segments]",
dash: true,
dashFlags: "[f=dash:window_size=3:extra_window_size=1]",
mp4: false,
mp4Flags: "[movflags=frag_keyframe+empty_moov]",
},
],
},
});
// hook 예시(권한/로그)
nms.on("prePublish", (id, path, args) => {
console.log("prePublish", { id, path, args });
// return false로 거절(버전/이벤트 시그니처는 패키지 문서 확인)
});
nms.run();
gop_cache: (구현 의존) GOP(키프레임) 캐시 — 끼어듦 감소 vs 지연/메모리 트레이드오프. 저지연 이면 끄거나 값을 측정하세요.trans+ FFmpeg: 다중 품질(ABR) 은ffmpeg필터체인(스케일/인코딩/맵/라벨/tee) 을 쓰는 쪽이 흔합니다(= CPU/메모리·큐 관리의 시작).
다중 화질(ABR) — 개념과 FFmpeg(예시)
서버에서 한 인풋을 3개 비트로 뿌릴 때의 뼈대(경로/코덱/프로파일은 실제 기준에 맞게):
# 개념 예시: 한 입력을 3개 vtrack + 1 atrack -> HLS(라벨/맵/버퍼)로 묶는 트릭
ffmpeg -i rtmp://in/app/key \
-filter_complex \
"[0:v]split=3[v1][v2][v3]; \
[v1]scale=1280:-2[v1o]; [v2]scale=960:-2[v2o]; [v3]scale=640:-2[v3o]" \
-map "[v1o]" -c:v:0 libx264 -b:v:0 3500k -maxrate:v:0 4000k -bufsize:v:0 8000k \
-map "[v2o]" -c:v:1 libx264 -b:v:1 2000k -maxrate:v:1 2500k -bufsize:v:1 5000k \
-map "[v3o]" -c:v:2 libx264 -b:v:2 800k -maxrate:v:2 900k -bufsize:v:2 2000k \
-map 0:a:0? -c:a aac -b:a 128k -ac 2 \
-f hls -hls_time 2 -hls_list_size 6 -hls_flags delete_segments+independent_segments \
-master_pl_name master.m3u8 -var_stream_map "v:0 v:1 v:2 a:0" \
/out/hls/stream_%v.m3u8
운영은 (1) GPU/노드 수, (2) 쿨다운/큐(백프레셔), (3) 키프레임 정렬(GOP/idr) 을 대시보드(큐/GOP drop/초기 버퍼) 와 함께 봅니다.
RTMP vs RTMPS vs RTMPT
- RTMP(일반): 1935/TCP(대개). 평문 이라서 캡슐(미디어·키) 노출/변조/재전송·프록시의 SNI/HTTPS 와 혼동 이슈에 취약(그래서 ingest 는 사설망·VPN/보안채널을 쓰는 팀이 많아요).
- RTMPS: RTMP + TLS(구 tcp/tls 래핑): “전송기밀+무결(경로/인증서/체인)만큼” — mTLS(클라이언트 인증서), SNI(멀티 도메인), TLS1.2/1.3·Ciphers 는 TLS 쪽과 동일 층의 고민(단, 지연(핸드셰이크·RTT), 오프로딩(하드/커널/KTLS), 세션 캐시/OCSP 영향)을 같이 봅니다.
- RTMPT(HTTP 터널): 방화벽/프록시 통과가 목적이었는데(옛 80/443 통과) HTTP 오버헤드·효율·보안(레거시) 측면에서 최우선으로는 덜 권하는 편(현대 스택: SRT, RTMPS, QUIC, WebRTC, LL-HLS 를 상황별 비교).
“청킹/버퍼/대역” 최적화(현실)
- 청크(프로토콜) vs TCP MSS vs 애플리케이션
chunk_size: 층이 다릅니다(혼동 금지). 문제(지연/CPU)를 어디서 재현했는지(인코더/서버/리버스/플레이어) 를 분리 측정하세요. - BWE/윈도·ACK(프로토콜) vs TCP
rwnd/cwnd: “신호”와 “혼잡”은 따로 움직입니다(그래서 대역초과·끼어듦·RTO 는ss,tc qdisc, BBR/BBRv2, 인코딩, GOP/버퍼 를 번갈아 봅니다). - “버퍼=안정, 지연=비용”: 인코더·서버(특히 HLS)
fragment/gop/gop_cache·플레이어buffer(초기/재시크)·CDN 캐시/샤딩 이 하나의 체입니다.
트러블슈팅(현장)
1) 연결 실패(빠른 점검표)
- (a) L4:
tcp/1935LISTEN/방화벽/보안그룹 - (b) L7(오해): HTTPS로 RTMP를 붙이기(TLS/포트) · SNI/인증서 · ALB/NLB/프록시(패시브 모드, 타임아웃)
- (c) App:
connect()됐는데 1~2초 내 RST = 핸드셰이크/앱 이 의심(프록시가 첫 바이트를 변경) - (d) Auth:
on_publish·콜백 2xx/필드명 — “TCP는 OK인데 403/401만” 패턴
2) 지연(latency) 최적화(순서)
- 인코더(키/GOP, B, 프로파일, VFR)
- 퍼스트프레임/버퍼(플레이어·HLS
hls_time) - 서버(큐,
gop_cache, A/V 정렬, 트랜스코딩 대기) - 경로(혼잡, 재전송, RTO) — Wi‑Fi/이동망/크로스리전
- OS(TCP:
nodelay/버퍼/큐) — 최후에 (근거 없이sysctl난사 금지)
3) 패킷 로스(체감)
TCP는 손실=재전송=지터; RTMP(비디오)는 I 프레임 끊기면 화가 나는 편(플레이어/디코더에 따라 멈춤/블록/스킵). SRT(ARQ/재전송 정책), 관측(재전송/RTT), ABR(비트로 낮춤) 가 도구.
4) 방화벽(정책)
- Egress(업로더→인터넷): 1935 아웃바운드
- Ingress(뷰어): HLS(HTTP/443)·CDN(엣지) — 뷰어와 퍼블리시 를 같은 포트로 혼동하지 말기
- 기업 HTTP 프록시: “HTTP/HTTPS는 OK인데 1935만 안 됨” = RTMPT 가 아닌 SRT/RTMPS/다른 터널/정책 이 나은 케이스가 많아요(보안/성능 함께 봄).
성능(스케일링) — 체크리스트
- CPU: 트랜스코딩·HLS fMP4/세그·FFmpeg 수 = 병목 1위가 흔함(특히 4K/고프레임). GPU/전용 박스(OME 등).
- MEM/DISK IO: HLS/녹화·세그
delete·NFS(특히 small file) — IOPS·캐시·로컬 SSD. - NET:
chunk_size만 만지기보다, BDP·RTO·RACK·BBR(환경), LB의 유휴 타임·TLS 세션·캡슐(ESP) 영향(멀캐 스트리밍은 따로). - 다중 프로세스/샤딩: 스트림 키 해시(일관성) / per-app 인스턴스 / edge ingest + core transcode — SPOF·빌링·장애도메인을 같이 설계.
- tcp 버퍼(커널): BDP(대역×RTT)의 2배(룰 오브 썸) 는 “출발점”일 뿐, 관측(ss -ti, drops, retrans, pacing) 이 없는 튜닝은 위험.
# (예시) 서버 BDP 100Mbps × 40ms = ~0.5MB급(대략) — "환경마다" 다름(측정 필수)
# Linux: /proc/sys/net/core/rmem_max, wmem_max, tcp_rmem, tcp_wmem, netdev_max_backlog
# 변경 전/후 iperf+실제 RTMP A/B(동시 n스트림)를 같이
권고:
sysctl은 그래프(체감, 큐, 드랍, RTO)와 함께 (그리고 롤백 가능하게).
정리
- RTMP는 “TCP+청크+AMF(제어)+AV(8/9 …)” 의 ingest 표준에 가깝고, 시청(브라우저) 는 HLS/LL-HLS/… 쪽이 현실.
- 운영은 (1) 핸드셰이크/콜백/필드명, (2) A/V/GOP/트랜스코딩 큐, (3) 경로의 혼잡/재전송 을 분리해서 보면 빨리 수렴합니다.
- RTMPS/RTMPT/SRT/LL-HLS/WebRTC 는 “대체가 아닌, 요구(지연/방화벽/브라우저/이동망)별 도구”로 두는 것이 합리합니다.
배포 전
git add,git commit,git push후npm run deploy를 수행하세요(프로젝트CLAUDE.md관행).
참고(공식/역사/구현)
- Adobe(레거시) RTMP/RTMPT/핸드셰이크/청크/메시징 문서(초기 스펙, 일부 공개 문서/구현 기반)
- Nginx-RTMP Module 문서(지시어, 콜백)
- FFmpeg
rtmp://,flvmuxer - Wireshark dissector( RTMP, AMF ) — 학습/장애에 매우 효율
이 문서는 운영/구현/체크리스트에 초점을 둡니다. 필드 단위(바이트) 레이아웃이 필요하면, 캡처(1935) + dissector 로 팀 내 공용 스냅샷을 만드는 것이 가장 정확합니다(구현/버전 차이가 있습니다).