🔥 Network ACL: Subnet 레벨 방화벽과의 차이
강의 목차

보안 그룹 한 장을 잘 적어 두면 NACL은 거의 손댈 일이 없다. 그런데도 VPC 콘솔에는 Network ACL 메뉴는 콘솔에 따로 한 칸을 두고 있고, 어떤 환경에서는 반드시 손이 가는 도구가 된다.
Subnet 경계에 놓인 한 장의 검문소
Security Group: 인스턴스 레벨 방화벽에서 본 SG는 ENI 한 장에 붙는다. NACL은 그보다 한 칸 바깥, Subnet 경계에 붙는다. 패킷은 Subnet에 들어오는 순간 NACL을 먼저 거치고, 그 다음에 ENI 앞에서 SG를 거치는 두 단계 통과 구조로 동작한다.
매달리는 규칙이 단순하다. Subnet 한 장에는 NACL이 정확히 한 개. 한 NACL은 여러 Subnet에 동시에 매달릴 수 있지만, 한 Subnet이 두 NACL을 동시에 매달지는 못한다. 그리고 NACL을 명시적으로 매달지 않은 Subnet은 VPC가 함께 만들어 준 default NACL이 자동으로 매달린 상태에서 출발한다.
여기서 한 가지 함정이 있다. VPC를 만들 때 함께 생기는 default NACL과 콘솔에서 새로 만든 custom NACL은 출발점이 정반대다. 이 비대칭은 뒤에서 한 번 더 짚는다.
Stateless라는 한 줄의 무게
NACL의 첫인상은 짧은 공식 문구 한 줄이 결정한다. "NACLs are stateless, which means that information about previously sent or received traffic is not saved." (verified 2026-04-26) 흐름을 기억하지 않는다는 뜻이다.
이게 운영에 무엇을 강요하는지가 진짜 이야기다. SG는 인바운드를 한 줄 허용하면 그에 대한 응답 outbound가 자동으로 통과한다. NACL은 아니다. 인바운드를 허용했다는 사실은 outbound 평가에 단 한 비트도 영향을 주지 않는다. 응답 패킷이 나가려면 outbound rule에 그 응답을 받아 줄 한 줄이 따로 적혀 있어야 한다.

문제는 응답이 어떤 포트로 돌아오느냐를 내가 정하지 못한다는 점이다. 클라이언트가 80포트로 요청을 보내면, 응답은 클라이언트가 잡은 ephemeral port 한 곳으로 돌아온다. 이 범위가 OS마다 다르다 (verified 2026-04-26).
- Linux 커널: 32768 – 61000
- Windows Server 2008 이상: 49152 – 65535
- Elastic Load Balancing / NAT Gateway / AWS Lambda: 1024 – 65535
운영에서 어떤 클라이언트가 들어올지 모르는 public-facing 환경이면 AWS 권고대로 1024–65535를 통째로 열어 두는 게 보통이다. 좁히면 어떤 OS의 클라이언트 응답이 끊기고, 끊기면 stateless라서 디버깅 흔적도 인스턴스 안에는 남지 않는다. SG에서는 신경 쓰지 않던 부분이, NACL에서는 매번 양방향 짝을 맞춰야 한다는 뜻이다.

번호 순서로 평가하는 첫-매치 방식
NACL 룰은 번호가 붙는다. 범위는 1부터 32766까지. AWS 공식 표현 그대로 "We evaluate the rules in order, starting with the lowest numbered rule, when deciding whether allow or deny traffic. If the traffic matches a rule, the rule is applied and we do not evaluate any additional rules."
첫 매치에서 즉시 판정이 끝난다는 점이 SG와는 다르다. SG는 허용 규칙 중 하나라도 일치하면 통과인 OR 연산이고, NACL은 번호 낮은 한 줄이 단독으로 결정을 정한다. 그래서 NACL은 한 줄의 위치가 결정 자체를 바꾼다. 100번에 deny가 적혀 있으면, 200번에 allow가 같은 트래픽을 적어 놨어도 그 트래픽은 deny로 끝난다.

번호의 끝에는 늘 한 줄이 위치한다. rule number가 별표(*). 1–32766 범위 바깥에 위치하고, 모든 트래픽 deny이며, 변경도 삭제도 불가다. 어떤 룰에도 매칭되지 않은 패킷은 이 한 줄이 패킷을 차단한다. NACL의 마지막 결정은 늘 침묵의 deny인 셈.
default NACL과 custom NACL은 출발점이 정반대
같은 이름의 객체지만 생성 경로에 따라 시작 구성이 정반대다.
- VPC를 만들 때 자동으로 생기는 default NACL: 인바운드 100번에 모든 IPv4 트래픽 allow, 아웃바운드 100번에 모든 IPv4 트래픽 allow, 그리고 양쪽 모두 끝에
*deny가 들어 있다. VPC에 IPv6 CIDR이 붙어 있으면 101번 라인에 IPv6 allow 한 줄이 더 들어간다. 결과는 모든 트래픽 통과. Subnet에 따로 NACL을 연결하지 않으면 AWS는 이 default NACL을 자동으로 연결해 평소와 같은 통과 동작을 유지한다. - 콘솔이나 API로 새로 만든 custom NACL: 인바운드
deny 한 줄, 아웃바운드deny 한 줄. 그게 끝이다. 연결되는 순간 NACL이 그 Subnet의 모든 트래픽을 차단한다.

이걸 모르고 "보안을 강화하려고 NACL을 새로 만들어 production Subnet에 매달았다"는 시나리오가 의외로 자주 사고를 친다. 새 custom NACL은 기본이 차단이라, 연결하는 순간 그 Subnet 위의 모든 인스턴스가 즉시 외부와의 통신을 잃는다. SG의 새 그룹이 0줄 인바운드 + 활짝 열린 아웃바운드로 시작했던 것과는 반대 방향의 위험이다.
룰 번호를 100, 200, 300으로 적는 건 미신이 아니다
AWS 공식 예제가 100, 105, 110, 115, 120 같은 5–10 단위로 번호를 잡는다. 권장 패턴은 단순하다. 나중에 사이에 끼워 넣을 여유를 남겨라. NACL은 한 줄을 원자적으로 다른 위치에 옮기는 명령이 없다. 옮기려면 같은 규칙을 새 번호로 추가하고 옛 번호의 규칙을 지우는 두 단계로 풀어야 한다. 그 사이 짧은 시간이 룰이 비어 있는 시간이고, stateless라 운영 중에는 그 짧은 공백을 신경 쓰게 된다.
100, 200, 300 단위로 잡고, 나중에 105, 110으로 끼워 넣고, 정말 빡빡해질 때만 재배치하는 패턴이 운영에 잘 통한다.
한도, SG와는 다른 곱셈
숫자 몇 개는 외워 두는 편이 낫다 (verified 2026-04-26).
- VPC당 NACL: 200개 (조정 가능)
- NACL당 인바운드 룰: 20줄 기본, 최대 40줄까지 조정 가능 (성능 영향 가능)
- NACL당 아웃바운드 룰: 20줄 기본, 최대 40줄까지 조정 가능
- IPv4 룰과 IPv6 룰은 AWS가 합산해서 한 카운트로 센다 (Security Group: 인스턴스 레벨 방화벽에서 본 SG와 다른 점: SG는 IPv4와 IPv6를 별개로 셌다)
SG가 ENI당 SGs × rules ≤ 1,000 한도까지 끌어쓸 수 있던 것과 비교하면 NACL은 훨씬 빡빡하다. 일상 ACL을 NACL에 적으려는 시도가 한도에서 막히는 이유도 같은 맥락이다. 한도 자체가 NACL은 일상 ACL이 아니라 굵직한 차단 한두 줄을 위한 영역이라는 설계 의도를 드러낸다.
그래서 언제 꺼내는가
NACL은 일상 보안 격리에 쓰는 도구가 아니다. 일반 트래픽 정책은 SG 한 장으로 거의 다 처리한다.
NACL이 진짜 필요한 시점은 좁다. 알려진 악성 IP 대역을 Subnet 단위로 통째로 차단해야 할 때. 또는 컴플라이언스 요건이 "Subnet 경계에 명시적 차단 룰을 반드시 적어야 한다고 명시할 때. 둘 다 SG의 허용만 적는다는 설계로는 풀리지 않는 시나리오다. 그게 NACL이 따로 존재하는 이유의 거의 전부다.
반대로, 일상 보안을 NACL에 박으려는 시도는 stateless 운영 부담을 떠안는 짓이 된다. 양방향 짝을 매번 맞춰야 하고, ephemeral 범위를 매 룰마다 의식해야 하고, 한도가 빡빡해서 룰이 빨리 차고, 첫-매치 방식이라 한 줄의 위치에 대한 인지 부담이 따라온다. SG로 풀 일을 NACL로 풀면 그 부담을 매일 진다.
ALB Health Check가 끊기는 흔한 함정
ALB가 매달린 Subnet의 NACL을 너무 좁게 잡으면 health check가 멈춘다. ALB의 ephemeral은 1024–65535이므로, target 인스턴스 Subnet의 outbound NACL에 그 범위가 비어 있으면 응답이 안 돌아간다. 모니터링이 왜 인스턴스가 살아 있는데 unhealthy로 빠지는가를 묻기 시작하면 NACL을 먼저 본다.
이건 모든 케이스를 커버하는 룰은 아니다. 단지 NACL을 처음 만질 때 가장 자주 밟는 함정이다. NACL은 굵게 한 줄을 그어야 할 때만 꺼내는 도구라는 것만 잡고 있으면, 평소에는 SG가 하던 역할에 그대로 머문다. 다음 편에서는 서로 다른 VPC가 어떻게 이어지는지가 핵심 관찰점이다.










