Azure VM으로 사내 동료 접속시키기 — 네트워크 입문

Public/Private IP, NAT, CIDR, NSG, bind 주소 — 한 줄 인바운드 룰의 5가지 전제

Azure VM 에서 도는 dev 서버에 사내 동료가 접속할 수 있게 하는 일견 단순한 작업이 IP 주소 종류, NAT, CIDR, NSG 인바운드 룰, dev 서버 bind 주소까지 5개 네트워크 기초를 모두 동원한다는 사실을 발견하고 정리한 입문 글.

Engineering
저자

Kwangmin Kim

공개

2026년 04월 27일

1 발단

“나 이거 다른 사람들도 접속해서 사용해봐야 하는데 어떻게 접속해야 해?”

MINERVA Platform(사내 AI Agent 플랫폼) 을 Azure VM(test-agent) 에서 돌리고 있었고, 사내 동료 몇 명이 시연 차원에서 접속해야 했다. 본인 노트북에서만 보던 화면을 옆 자리 사람이 자기 PC 에서 같은 URL 로 열게 하는 작업이다.

일견 단순하지만 이 작업 하나에 다음 개념이 다 얽힌다 — IP 주소 종류, 포트, NAT, CIDR, NSG 인바운드 룰, dev 서버 bind 주소, Vite proxy. 네트워크 지식 없이 시작했고, 끝나고 보니 “내가 뭘 한 거지?” 가 남아 정리한다.

2 우리가 결국 한 일 — 한 줄 요약

Azure VM test-agent 의 NSG(test-agent-nsg) 인바운드 룰에 TCP 5173 포트, Source 61.74.175.54/32 Allow 한 줄을 추가했더니, 같은 회사에 있는 모든 동료가 http://<test-agent-Public-IP>:5173 로 접속 가능해졌다.

이 한 줄을 이해하려면 알아야 할 5개 개념이 있다.

3 알아야 할 5가지 네트워크 기초

3.1 IP 주소 — Public 과 Private 의 차이

IP 주소 는 네트워크 상의 컴퓨터에게 붙는 식별자다. IPv4 의 경우 4개의 숫자로 표현된다 (예: 61.74.175.54).

종류 예시 대역 의미
Private (사설) 10.x.x.x, 172.16~31.x.x, 192.168.x.x 사내 LAN 안에서만 통하는 IP. 인터넷에선 직접 못 봄. RFC1918 표준에 따라 “내부용” 으로 예약된 대역
Public (공인) 그 외 모든 대역 인터넷 전체에서 유일. ISP(통신사)가 회사·집에 할당. 라우터가 인터넷 향해 나갈 때 쓰는 대표 IP

본인 PC 에서 두 종류 모두 확인 가능하다.

  • 사설 IP: ipconfig(Windows) 또는 ifconfig/hostname -I(Linux) → 10.11.184.211 같은 값. 본인 PC 가 사내 LAN 에서 부여받은 IP
  • 공인 IP: 브라우저로 https://api.ipify.org 접속 → 61.74.175.54 같은 값. 인터넷 상대 서버가 본 본인 PC 의 IP — 사실은 본인 PC 의 IP 가 아니라 회사 게이트웨이의 IP

이 둘이 다른 이유는 다음 절의 NAT 때문이다.

3.2 포트 (Port)

IP 가 “건물 주소” 라면 포트는 “그 건물의 호실”이다. 한 컴퓨터(IP) 안에서 여러 서비스가 동시에 돌 수 있도록 구분하는 16비트 숫자(0~65535)다.

포트 일반 용도
22 SSH
80 HTTP
443 HTTPS
5173 Vite dev server (관례)
8000 FastAPI (관례)

http://61.74.175.54:5173 이라고 쓰면 “61.74.175.54 IP 컴퓨터의 5173 호실에 HTTP 로 노크” 라는 뜻이다. 같은 IP 라도 5173 과 8000 은 완전히 다른 서비스를 가리킨다.

3.3 NAT — 사무실 PC 들이 같은 공인 IP 를 공유하는 이유

“내 동료도 61.74.175.54 이거래”

같은 사무실에서 다른 사람이 https://api.ipify.org 를 열어도 같은 공인 IP 가 나온다. 이게 우연이 아니라 NAT (Network Address Translation) 라는 표준 동작이다.

작동 방식은 다음과 같다.

[ 본인 PC  10.11.184.211 ]    ┐
[ 동료 PC  10.11.184.212 ]    ├──→ [ 회사 라우터 ] ──공인 IP 61.74.175.54──→ 인터넷
[ 동료 PC  10.11.184.213 ]    ┘                       (NAT)

회사 라우터가 모든 사내 PC 의 인터넷 트래픽을 자기 공인 IP 61.74.175.54 한 개로 변환해서 내보낸다. 인터넷 상대방은 이 트래픽이 어느 사내 PC 에서 왔는지 모르고 그냥 “회사 게이트웨이” 한 곳에서 온 걸로 본다. 응답이 돌아오면 라우터가 (포트 매핑 테이블 보고) 원래 사내 PC 에게 전달한다.

이게 사무실 모든 PC 가 단 하나의 공인 IP 를 공유하는 비결이고, IPv4 주소 부족 문제를 해결하는 핵심 메커니즘이기도 하다.

우리 작업에 미친 영향: NSG 의 Source IP 화이트리스트에 61.74.175.54/32 딱 한 개만 넣어도 회사 사무실 전체 PC 가 통과한다. 인터넷 상대방(Azure)은 우리 회사 트래픽이 한 IP 에서만 오는 걸로 보기 때문이다.

3.4 CIDR 표기 — /32, /24, /16 의 의미

NSG 룰에서 Source 를 61.74.175.54/32 처럼 쓴 그 슬래시 숫자가 CIDR (Classless Inter-Domain Routing)이다.

/N = “IP 의 앞에서부터 N비트는 고정, 뒤 (32-N)비트는 자유”

IPv4 는 32비트이므로 N 은 0~32 사이의 값을 가진다.

CIDR 의미 포함 IP 개수
203.0.113.5/32 앞 32비트 전부 고정 = 정확히 그 한 IP 1개
203.0.113.0/24 앞 24비트 고정, 뒤 8비트 자유 = .0 ~ .255 256개
203.0.113.0/16 앞 16비트 고정 = 203.0.0.0 ~ 203.0.255.255 65,536개
0.0.0.0/0 0비트 고정 = 모든 IP 약 43억개 (전체 인터넷)

직관적으로는 “/N 의 N 이 클수록 좁은 범위”로 기억하면 된다.

회사 IP 61.74.175.54 한 개만 허용하고 싶으면 /32 (“정확히 이 IP”) 가 맞다. 만약 회사가 61.74.175.0 부터 61.74.175.255 까지 256개를 다 쓴다면 61.74.175.0/24 로 한 번에 묶는다.

경고

0.0.0.0/0전체 인터넷 허용이다. 시연 임시용으로 쓰면 편하지만 즉시 외부 공격 노출이라 실서비스에서는 절대 금물이다.

3.5 방화벽 — 네트워크의 출입국 심사대

방화벽 (Firewall) 은 “어떤 IP·포트에서 오는 트래픽을 통과시킬지/막을지” 결정하는 룰의 집합이다.

비유하자면 사무실 빌딩 1층 출입국 심사대다. 들어오는 사람(트래픽) 마다 “어디서 왔는지 (Source IP)”, “어느 호실 가려는지 (Destination Port)” 확인하고 화이트리스트에 있으면 통과, 없으면 차단한다.

여러 층위에 방화벽이 있다.

층위 어디서 동작
클라우드 레벨 Azure / AWS 경계 NSG, Security Group
VM OS 레벨 VM 운영체제 Windows Defender Firewall, ufw, iptables
애플리케이션 레벨 앱 자체 Express CORS, FastAPI 미들웨어

VM 까지 트래픽이 도달하려면 모든 층위를 통과해야 한다. NSG 만 열고 OS 방화벽 안 열어도 막힌다.

4 Azure 네트워크의 구성요소

이제 Azure 쪽 용어다. 위 5개 개념을 Azure 가 어떻게 이름 붙였는지 매핑해본다.

4.1 Virtual Network (VNet)

Azure 가 가상으로 만든 사내 LAN 이다. VM 들이 같은 VNet 안에 있으면 사설 IP 끼리 직접 통신 가능하다. 사내 회사 LAN 의 클라우드 버전이라고 보면 된다.

test-agent VM 이 어느 VNet 에 속하는지는 Azure portal → VM → Networking 에서 확인한다.

4.2 NIC (Network Interface)

VM 의 가상 네트워크 카드다. VNet 에 꽂혀있고, 사설 IP 와 (선택적으로) 공인 IP 가 여기에 붙는다.

리소스 이름 test-agent338_z1 처럼 이름에 VM 이름이 들어가 있다.

4.3 Public IP 주소

VM 을 인터넷에서 접근 가능하게 하려면 Public IP 가 NIC 에 attach 돼 있어야 한다. 처음엔 리소스 목록을 보고 “test-agent 는 Public IP 없는 줄 알았는데” 실제로는 NIC 에 직접 붙어있어서 별도 리소스로 안 보였던 케이스다.

확인 방법: Azure portal → VM → Overview 또는 Networking → “Public IP address” 항목.

4.4 Network Security Group (NSG)

NSG = Azure 의 방화벽 룰 집합이다. NIC 또는 서브넷에 attach 돼서 거기 들어오는·나가는 트래픽을 검사한다.

test-agent-nsg 가 그것이다. 한 NSG 안에 여러 룰이 있고, 각 룰은 다음을 명시한다.

  • Source: 어디서 오는 트래픽? (IP, CIDR, 또는 서비스 태그)
  • Source port: 보통 * (any) — 클라이언트 측 포트는 랜덤
  • Destination: 보통 Any 또는 VM 자체
  • Destination port: 어느 포트로 들어가나? (예: 5173)
  • Protocol: TCP / UDP / Any
  • Action: Allow (허용) / Deny (차단)
  • Priority: 100~4096. 숫자가 작을수록 먼저 평가. 한 룰에 매칭되면 그 룰의 Action 이 결정

4.5 인바운드 vs 아웃바운드

  • 인바운드 (Inbound): 외부 → VM 으로 들어오는 트래픽. 사내 동료 접속을 받기 위해 5173 을 여는 건 인바운드 룰이다
  • 아웃바운드 (Outbound): VM → 외부로 나가는 트래픽. Azure OpenAI API 호출 같은 게 여기 해당된다. 보통 기본값(다 허용) 으로 둔다

5 서버 사이드 — bind 주소

NSG·VM 이 다 열려도 서버 자체가 “외부 인터페이스로 못 들어오게” 설정돼 있으면 막힌다. 이게 bind 주소다.

5.1 dev 서버 (npm run dev) 가 도대체 뭘 하는가

npm run dev 는 사실 Vite 라는 dev 서버를 띄우는 명령이다. React 코드 자체가 서버를 들고 있는 게 아니라, 브라우저에 React 앱을 전달해주는 별도 HTTP 서버가 필요하다.

모드 하는 일 산출물
npm run dev Vite 가 5173 포트에 HTTP 서버 기동. .tsx 파일 변경 감지 → 브라우저로 HMR push (Hot Module Replacement) 메모리 (디스크 안 씀)
npm run build .tsx 전체를 1회 변환·번들링·압축 → 정적 HTML/JS/CSS 생성 dist/ 폴더 (정적 파일)

dev 서버의 핵심은 브라우저가 5173 으로 GET 요청을 보내면 Vite 가 그 시점에 .tsx.js 즉석 변환해서 응답한다는 점이다. 매번 다시 빌드 안 해도 된다. 그래서 npm run dev 로 띄워둔 채로 코드를 수정하면 브라우저가 0.5초 안에 반영된다 (HMR).

프로덕션은 다르다. npm run build 로 만든 dist/ 를 Nginx 같은 정적 서버나 FastAPI 의 StaticFiles 마운트로 서빙한다. Node.js 도 Vite 도 운영 서버에는 필요 없다.

5.2 127.0.0.1 vs 0.0.0.0

서버를 띄울 때 어느 IP 로 들어오는 연결을 받을지 지정한다.

bind 주소 의미 누가 접근 가능?
127.0.0.1 (= localhost) loopback (“자기 자신”) 같은 머신 안의 프로세스만 (절대 외부 안 보임). SSH 터널은 예외
0.0.0.0 모든 인터페이스 같은 머신 + 같은 LAN + 외부 IP 다 받음
172.16.0.4 같은 구체 IP 특정 인터페이스만 그 인터페이스 통해 들어오는 연결만

비유하자면 사무실 전화기에 “내선만 받기 / 외선까지 받기 / 특정 외선만 받기” 모드가 있는 것과 같다. 127.0.0.1 은 “내선 전용”, 0.0.0.0 은 “전부 다 받음”이다.

이게 가장 자주 만나는 함정이다. NSG 룰 다 열고 동료에게 URL 알려줬는데 “접속 안 돼요” 면 십중팔구 서버가 127.0.0.1 로 떠있다.

5.3 Vite 와 uvicorn

  • Vite: vite.config.tsserver.host: '0.0.0.0' 설정으로 외부 인터페이스 허용. Vite 의 기본값은 127.0.0.1 인데, 보안상 합리적이다 — dev 서버는 인증 없는 거라 외부 노출 위험
  • uvicorn (FastAPI): 명령줄 옵션 --host 0.0.0.0 으로 명시. 기본값은 127.0.0.1 이라 별도 설정 없으면 외부 차단
# 외부 노출
uvicorn services.api.main:app --host 0.0.0.0 --port 8000

# 자기 자신만
uvicorn services.api.main:app --host 127.0.0.1 --port 8000

Makefile 에 변수로 빼서 default 0.0.0.0, 보안 모드는 make dev-backend HOST=127.0.0.1 로 토글 가능하게 두면 편하다.

5.4 Vite proxy 가 한 일 — backend 를 외부에 안 열어도 되는 이유

브라우저는 Vite (5173) 에만 접속하지만, 사실 RAG API 는 FastAPI (8000) 에 있다. 그러면 8000 도 외부에 열어야 할까? 아니다. Vite dev server 의 proxy 기능 덕분이다.

브라우저 → Vite (5173, VM 외부 노출) ──proxy──→ FastAPI (127.0.0.1:8000, VM 안)
                                                     ↑ VM 안끼리 loopback OK

vite.config.ts 에 명시된 proxy 설정은 다음과 같다.

server: {
  proxy: {
    '/agents':     { target: 'http://localhost:8000' },
    '/monitoring': { target: 'http://localhost:8000' },
    '/feedback':   { target: 'http://localhost:8000' },
    // ...
  },
}

브라우저가 /agents/qna_chatbot/stream 으로 요청을 보내면 Vite 가 받아서 VM 안에서 localhost:8000/agents/qna_chatbot/stream 으로 다시 요청한다. 응답을 받아 브라우저에 전달한다.

브라우저 입장에선 Vite 가 다 처리하는 것처럼 보인다. NSG 인바운드는 5173 만 열면 되고, 8000 은 외부에 안 노출되니 보안 우위다.

(부수 효과) CORS 신경 안 써도 됐던 이유: 브라우저가 본 origin 이 한 개 (http://<Public-IP>:5173) 라 cross-origin 요청 자체가 발생 안 한다. 만약 브라우저가 직접 :8000 호출했다면 CORS 미들웨어 설정이 필요했을 것이다.

6 우리가 거친 작업 — 시간순

처음부터 정리한다.

6.1 Step 1. 시나리오 결정

후보는 셋이었다.

  • A. 사내망 동료들 (LAN/VPN) — 가장 빠름, 임시 시연용
  • B. production 빌드 + StaticFiles 통합 — 8000 단일 포트
  • C. 사내 통합 플랫폼(tech-manage-web) iframe 통합 — 정식 사내 통합

5월 발표 직전이라 A 채택. B/C 는 추후로 미뤘다.

6.2 Step 2. Vite·uvicorn bind 점검

  • vite.config.tsserver.host — 이미 0.0.0.0 이라 변경 없음
  • Makefile dev-backend — uvicorn 에 --host 미지정 (기본 127.0.0.1) → HOST ?= 0.0.0.0 변수 추가해서 default 외부 노출
HOST ?= 0.0.0.0
PORT ?= 8000

dev-backend:
    poetry run uvicorn services.api.main:app --reload --host $(HOST) --port $(PORT)

6.3 Step 3. NSG 인바운드 룰 추가 (Azure portal)

test-agent-nsg → Inbound security rules → Add:

필드
Source IP Addresses
Source IP / CIDR (다음 단계에서 결정)
Source port *
Destination Any
Destination port 5173
Protocol TCP
Action Allow
Priority 1010
Name Allow-MINERVA-FE-5173

6.4 Step 4. Source IP 결정 — 가장 헷갈렸던 부분

여기서 Public/Private IP, NAT, CIDR 개념이 다 동원된다.

본인 PC 의 두 IP: - 공인 IP (api.ipify.org): 61.74.175.54 - 사설 IP (ipconfig): 10.11.184.211

처음엔 “사설 IP 를 넣어야 하나? 공인 IP?” 헷갈렸다. 결정 기준은 다음과 같다.

트래픽이 Azure 까지 갈 때 마지막에 보이는 IP 가 어떤 거냐

  • 회사 인터넷 → Azure Public IP 경유 → 회사 라우터의 NAT 거쳐 공인 IP 61.74.175.54 로 보임
  • 사내 ↔︎ Azure VPN/ExpressRoute 직결 환경 → 사설 IP 가 그대로 보임

test-agent 가 알고 보니 Public IP 가 NIC 에 붙어있었고, 회사 PC 가 인터넷 통해 그 Public IP 로 접근하는 경로였다. 그러면 NAT 거친 후 Source 는 공인 IP 61.74.175.54 다.

6.5 Step 5. 동료 IP 확인 → 한 줄 화이트리스트 도출

“내 동료도 61.74.175.54 이거래”

NAT 덕분이다. 사무실 모든 PC 가 같은 공인 IP 공유 → 61.74.175.54/32 한 개로 회사 전체 커버.

NSG 룰의 Source 에 61.74.175.54/32 입력 → Add. 적용까지 1~2초.

6.6 Step 6. 접속 테스트 — 성공

본인 PC 에서 브라우저 → http://<test-agent-Public-IP>:5173 → 정상 동작. 동료 PC 에서도 같은 URL → 정상 동작.

7 왜 한 줄로 충분했나 — NAT + 화이트리스트

핵심 통찰을 다시 정리하면 다음과 같다.

사무실 모든 PC ──NAT──→ 공인 IP 61.74.175.54 ──인터넷──→ Azure VM
                              ↑
                  NSG 가 보는 Source IP

NSG 입장에서 사무실 PC 들이 다 하나의 IP 로 보인다. 화이트리스트도 한 개면 끝이다.

만약 동료가 다른 사무실(다른 회사 라우터) 또는 재택(집 인터넷) 이라면 다른 공인 IP 라서 별도 룰이 필요하다.

8 보안 측면

/32 한 개 화이트리스트의 효과는 다음과 같다.

  • 외부 인터넷 차단: 인터넷 어디선가 5173 포트 스캔해도 NSG 가 Source 매칭 실패 → drop. 회사 IP 로 위장하려면 IP spoofing 인데 TCP 연결에선 응답을 받을 수 없어 사실상 불가능
  • 사무실 내부 한정: 회사 외부 (집, 카페 등) 에선 접속 불가. 동료가 재택근무 시 못 봄
  • 포트 5173 만: 다른 포트(예: 8000) 는 NSG 룰 없으니 외부 차단 유지
경고

만약 0.0.0.0/0 로 잘못 열었다면 즉시 전 세계 봇이 5173 스캔해서 dev server 의 알려진 취약점(Vite 의 /__open-in-editor 같은 dev 엔드포인트 등) 시도가 들어온다. 짧은 시간이라도 위험하다.

9 한계와 다음 단계

이번 설정은 시연용 임시 노출이다.

  • Vite dev server 는 production 부적합 (HMR, sourcemap, 큰 번들, 느림). 일 단위 시연 OK, 주 단위 운영 X
  • HTTPS 없음 — http:// 라 트래픽 평문. 사내망 한정이라 큰 문제는 없지만 이상적이진 않음
  • 인증 없음 — /docs (FastAPI Swagger) 가 외부에 노출됨. 백엔드도 0.0.0.0 으로 떠 있으면 위험. 보안 모드는 make dev-backend HOST=127.0.0.1

다음 단계 후보는 다음과 같다.

  • 시나리오 B: make build-frontend 로 정적 번들 만들어서 FastAPI 가 직접 서빙. 8000 단일 포트, Node.js 운영 서버 불필요
  • 시나리오 C: 사내 통합 플랫폼(tech-manage-web) 안에서 iframe 으로 띄움. NextAuth SSO 연동, 사내 도메인 사용
  • HTTPS: Caddy / nginx reverse proxy + Let’s Encrypt 또는 Azure App Service 의 자동 HTTPS

운영 단계로 가면 NSG 도 더 다듬어진다 — 5173 닫고 443 (HTTPS) 만 열기, 사내 IP 화이트리스트 + Azure AD 인증 더하기 등이다.

10 정리 — 트래픽 추적이 90%

네트워크 작업의 99% 는 “트래픽이 어느 IP·어느 포트로 어디까지 가나” 를 추적하는 일이다.

클라이언트 IP → NAT 거쳐 공인 IP 변환 → Azure NSG → VM NIC → OS 방화벽 → 서버 bind 주소 → 애플리케이션

막히면 어느 단계에서 막혔는지 차근차근 추적한다. 이 흐름만 잡혀 있으면 처음 보는 환경에서도 적용 가능하다. NSG, NAT, CIDR 다 이 흐름을 컨트롤하는 도구일 뿐이다.

트러블슈팅 체크리스트 (위에서 아래로)
  1. 로컬 PC → 인터넷: curl https://api.ipify.org 로 공인 IP 확인. 회사 외부면 NAT 가 다른 IP 로 보낸다는 뜻
  2. NSG 인바운드: Azure portal → NSG → Inbound rules. Source IP·Port·Protocol 모두 매칭되는지
  3. VM OS 방화벽: ufw status(Linux) / Windows Defender. 클라우드 NSG 통과해도 OS 가 막을 수 있음
  4. 서버 bind 주소: ss -lntp | grep 5173 (Linux) 또는 netstat -an | findstr 5173 (Windows). 127.0.0.1:5173 이면 외부 안 보임. 0.0.0.0:5173 또는 *:5173 이어야 함
  5. proxy 설정 (있다면): Vite proxy 가 backend 로 잘 라우팅하는지 — 브라우저 DevTools Network 탭에서 5173 으로만 요청 나가는지 확인

11 관련 주제

Subscribe

Enjoy this blog? Get notified of new posts by email: