티스토리 뷰

들어가며

SG와 NACL이라는 두 도구를 한 VPC 위에 함께 두는 일을 두고, 처음 설계 단계에서 가장 자주 듣는 질문은 "둘 다 두면 중복 아닌가요"라는 것이었습니다. 한쪽이 막는 자리를 다른 쪽이 또 막는 모양처럼 보이는 자리가 분명히 있고, 같은 트래픽이 두 번 검사받는 것처럼 느껴지는 것도 사실입니다. 그래서 이 글은 두 도구가 어디에 붙어 있고, 무엇을 다르게 막는지를 한 번 정직하게 정리해 보려는 글입니다. 비유 한 줄로 시작해 보자면, SG는 호텔의 객실 출입 통제이고 NACL은 호텔 정문의 검문에 가까운 위치에 자리 잡고 있다고 느꼈습니다. 같은 사람을 두 번 마주치는 듯해도, 한 자리는 ENI라는 객실의 문 앞이고 다른 한 자리는 서브넷이라는 정문의 검문대인 셈입니다.

 

이 글은 운영 단계에서 흘러간 사고의 회고가 아니라, 개인 프로젝트로 VPC를 설계하고 IaC로 그려 두는 과정에서 정리한 합계와 가정을 담은 글이라는 점을 먼저 밝혀 둡니다. 사실관계는 가능한 한 AWS 공식 문서가 안내하는 범위 안에서만 다루고, 그 밖의 결은 결정의 근거로 정리해 두는 정도로만 두려 합니다. 글의 척추는 단순합니다. 두 도구가 같은 우산 아래에 있어 보여도 책임이 분리되어 있다는 점, 한쪽이 우회되어도 다른 한쪽이 남는다는 점, 그리고 그 책임 분리가 운영 자동화와 변경 관리에서 어떻게 다른 무게로 다가오는지를 따라가 보려 합니다.

핵심 정리. SG와 NACL은 같은 트래픽을 두 번 검사하는 도구가 아니라, ENI와 서브넷이라는 두 자리에서 서로 다른 책임을 나눠 지는 도구입니다. 한쪽이 우회되어도 다른 한쪽이 남는 자리에서, 두 개의 도구는 비로소 심층 방어가 됩니다.

 


 

두 도구를 한 번에 두는 이유 — 책임이 다르다

AWS 공식 문서는 Security Group을 ENI 단위에서 동작하는 가상 방화벽으로 정의하고 있습니다. ENI는 Elastic Network Interface의 줄임말이며, EC2 인스턴스나 ECS Task의 네트워크 끝에 붙는 가상의 네트워크 카드라고 보면 결을 잡기 쉽습니다. SG는 그 ENI 한 장 한 장에 부착되어, 어떤 트래픽이 그 인터페이스로 들어올 수 있고 나갈 수 있는지를 통제합니다. 같은 서브넷 안에서도 ENI마다 다른 SG가 붙을 수 있다는 점이 이 도구의 가장 큰 결입니다.

 

Network ACL은 다른 자리에 자리 잡고 있습니다. AWS 공식 문서가 안내하는 정의에 따르면 NACL은 서브넷 단위에서 동작하는 스테이트리스 방화벽입니다. 서브넷 안의 모든 ENI를 거쳐 가는 트래픽이 NACL의 룰을 한 번 통과해야 합니다. 한 서브넷에는 정확히 하나의 NACL이 부착되며, 같은 NACL을 여러 서브넷에 부착할 수도 있습니다. ENI 단위의 SG와 비교하면 결이 한 단계 위에서 동작하는 도구라는 인상이 강하게 옵니다.

 

두 도구를 함께 두는 이유는 책임이 서로 다르기 때문이라는 점이 여기서 자연스럽게 따라옵니다. SG는 "이 ENI에 닿아도 되는 트래픽인가"를 묻는 자리이고, NACL은 "이 서브넷에 들어와도 되는 트래픽인가"를 묻는 자리입니다. 어떤 사고로 SG가 잘못 설정되더라도 NACL이 한 번 더 걸러 주고, 반대로 NACL이 너무 느슨하게 풀려 있더라도 SG가 ENI 단위에서 다시 한 번 좁혀 줍니다. 한 도구의 사고가 다른 도구로 번지지 않도록 책임을 두 자리에 나누어 둔 결정이라고 정리해 두려 합니다.

 

두 도구의 기본값이 다르다는 점도 결정의 회계에 영향을 줍니다. AWS 공식 문서에 따르면 SG는 모든 인바운드 트래픽이 기본 거부이고, 명시적으로 허용한 트래픽만 ENI에 도달할 수 있습니다. NACL은 사용자가 직접 만든 NACL의 경우 마찬가지로 기본 거부로 시작하지만, AWS가 제공하는 기본 NACL은 모든 트래픽을 허용하도록 초기화되어 있다는 점이 함정처럼 작동할 수 있습니다. 새 서브넷을 만들고 별도의 NACL을 부착하지 않으면 그 서브넷은 기본 NACL의 "모두 허용"을 그대로 따라가게 되므로, 사설 데이터 서브넷처럼 외부 노출을 닫아 두어야 할 자리에서는 별도의 NACL을 명시적으로 부착해 두는 결정이 따라붙어야 합니다. 이 작은 차이가 운영 사고로 번지는 자리이기도 해서, 본 프로젝트에서는 모든 서브넷에 명시적으로 만든 NACL을 부착하도록 설계해 두었습니다.

 


 

스테이트풀이 운영을 줄여 주는 자리

SG가 스테이트풀이라는 점은 운영의 무게를 가장 많이 줄여 주는 자리입니다. 스테이트풀 방화벽은 들어오는 트래픽에 대해 룰을 한 번 평가하고, 그 트래픽에 대한 응답을 자동으로 허용해 주는 동작을 합니다. AWS 공식 문서가 안내하는 바에 따르면 SG는 인바운드 룰만 정확히 적어 두면, 그 트래픽에 대한 응답이 어떤 임시 포트 번호로 돌아오든 룰을 추가로 작성하지 않아도 통과합니다. 같은 결로 아웃바운드 룰만 적어 두면 그 트래픽에 대한 응답이 어떤 포트로 돌아오든 자동으로 허용됩니다.

 

이 동작이 작아 보여도 운영의 결을 크게 바꿉니다. 클라이언트가 외부 서비스를 호출할 때, 운영체제는 1024부터 65535 사이의 임시 포트(ephemeral port) 한 개를 골라 출발 포트로 사용합니다. 그 임시 포트가 어떤 번호로 잡힐지는 호출 시점의 OS 결정에 달려 있어 사전에 알 수 없습니다. SG가 스테이트풀이기 때문에 이 임시 포트의 범위를 따로 외워 두지 않아도 응답 트래픽이 자연스럽게 통과됩니다.

 

NACL은 다른 자리입니다. AWS 공식 문서가 명시적으로 안내하는 바와 같이 NACL은 스테이트리스 방화벽이고, 인바운드와 아웃바운드 룰을 모두 명시해야 합니다. 클라이언트가 외부로 나가는 트래픽을 허용하려면 아웃바운드 룰을 적어야 하고, 그 응답이 돌아오는 트래픽을 허용하려면 인바운드 룰에 임시 포트 범위(1024–65535)를 한 번 더 적어야 합니다. 룰을 한 줄 빠뜨리면 응답이 돌아오지 않아 호출이 멈추는 결의 사고가 발생할 수 있고, 처음 NACL을 그리는 자리에서 가장 자주 빠뜨리는 자리이기도 합니다.

 

스테이트풀이 주는 운영의 가벼움은 여기서 끝나지 않습니다. ENI마다 부착되는 SG는 워크로드의 호출 그래프를 그대로 따라가는 결로 작성될 수 있고, 응답 트래픽을 따로 적어 두지 않아도 되니 룰의 양 자체가 작게 유지됩니다. NACL은 서브넷 단위로 한 번에 적용되는 만큼 룰이 너무 세밀해지면 가독성이 빠르게 떨어지므로, 두 도구의 결을 따라가다 보면 자연스럽게 SG는 정밀한 통제, NACL은 서브넷 단위의 큰 자물쇠로 자리 잡게 됩니다.

 

NACL의 룰 평가 방식 자체가 SG와 다른 결을 가지고 있다는 점도 짚어 둘 만합니다. AWS 공식 문서가 안내하는 바에 따르면 NACL은 룰 번호의 오름차순으로 평가되며, 가장 먼저 매칭되는 룰의 결정(허용 또는 거부)이 즉시 적용되고 그 뒤의 룰은 더 이상 평가되지 않습니다. SG는 어느 한 룰이라도 트래픽을 허용하면 통과되는 결의 평가 모델이고, 명시적인 거부 룰을 작성할 수 없는 구조입니다. 이 차이는 작은 운영 사고에서도 다르게 드러납니다. NACL에는 임시로 특정 IP를 거부 목록에 올려 두는 결의 표현이 가능한 자리이고, SG에는 그런 표현이 처음부터 존재하지 않습니다. 거부의 표현이 필요한 자리에서는 NACL이 자연스럽게 등장하고, 허용 그래프를 정밀하게 좁히는 자리에서는 SG가 더 잘 맞는 결이라고 정리해 두면 두 도구의 자리가 비교적 잘 그려집니다.

 


 

SG ID 참조가 베스트프랙티스가 아니라 전제가 되는 자리

SG가 다른 도구들과 결정적으로 갈리는 자리는 룰의 소스를 IP가 아닌 SG ID로도 적을 수 있다는 점입니다. AWS 공식 문서에 따르면 SG의 인바운드 룰에는 CIDR 블록뿐 아니라 다른 SG의 ID를 소스로 적을 수 있고, 그 룰의 의미는 "그 SG가 부착된 ENI에서 오는 트래픽을 허용한다"가 됩니다. 같은 결로 아웃바운드 룰에도 다른 SG ID를 목적지로 적을 수 있습니다.

 

이 표현이 가지는 무게는 컨테이너 워크로드를 띄우는 자리에서 가장 분명하게 드러납니다. Fargate 같은 컨테이너 환경에서는 Task가 죽고 살아나기를 반복하면서 ENI의 사설 IP 주소가 계속 바뀝니다. 만약 SG의 룰이 CIDR 블록으로 적혀 있다면, 새로 띄워진 Task의 IP가 그 블록 밖으로 떨어지는 순간 호출이 막히는 사고가 일어납니다. SG ID로 룰을 적어 두면 IP가 어떤 값으로 잡히든 문제가 되지 않습니다. 새 Task에도 같은 SG가 붙어 있는 한, 그 Task에서 오는 트래픽이 자동으로 허용됩니다.

 

이 점이 본 시리즈의 결과 같은 자리에 놓이는 이유는, "SG ID로 참조하라"는 한 줄이 단순한 베스트프랙티스가 아니라 운영 자동화의 전제가 된다는 점입니다. CIDR 기반의 룰은 사람이 추가하고 사람이 빼는 결정 회계 위에 서 있고, SG ID 기반의 룰은 어떤 워크로드가 어떤 워크로드를 호출하는가라는 역할 그래프 위에 서 있습니다. Auto Scaling, Fargate, EKS 같은 동적 워크로드 환경에서는 후자의 결이 자연스럽게 잘 맞고, 운영자가 IP 주소를 추적하지 않아도 호출 관계가 그대로 유지된다는 점이 이 도구의 가장 큰 가치라고 느꼈습니다.

 

온프레 환경에서는 이 결이 다르게 그려졌던 기억이 있습니다. 코어 방화벽의 룰을 작성할 때 보통 출발지를 IP 대역으로 적었고, 새로운 호스트가 사내망에 들어오면 그 호스트의 IP를 룰에 추가하는 결정 회계가 따라붙었습니다. AWS의 SG ID 참조는 그 결을 한 단계 추상화 위로 올려 둔 도구라고 보입니다. "어떤 IP에서 들어오는가"를 묻던 자리가 "어떤 역할을 가진 워크로드가 들어오는가"를 묻는 자리로 옮겨진 셈이고, 그 역할이 새 IP로 띄워지더라도 룰이 그대로 유지된다는 점이 클라우드 보안 모델의 가장 큰 특징 중 하나라고 정리할 수 있을 것 같습니다.

 


 

한 SG의 매트릭스를 읽어 보면 — sg-prod-api-gateway의 두 방향

이 프로젝트의 02 문서에서 정리한 SG 매트릭스 중 한 자리를 가져와 보면, 두 도구의 결이 어떻게 호출 그래프와 맞물리는지가 비교적 분명하게 드러납니다. API Gateway 앞에 두는 SG, 즉 sg-prod-api-gateway의 인바운드와 아웃바운드를 함께 보면, 이 한 줄의 룰이 어디에서 와서 어디로 가는 트래픽을 허용하는지가 한눈에 따라옵니다.

방향 소스 포트 비고
인바운드 sg-prod-alb 443 ALB만 게이트웨이를 호출할 수 있도록 한 자리
아웃바운드 sg-prod-api-auth 8083 인증 서비스 호출
아웃바운드 sg-prod-api-chatbot 8084 챗봇 서비스 호출
아웃바운드 sg-prod-api-bookmark 8085 북마크 서비스 호출

 

이 매트릭스를 보면, API Gateway 앞에 있는 ALB의 SG가 유일한 인바운드 소스라는 점이 가장 먼저 눈에 들어옵니다. 어떤 IP에서 오든, 어떤 서브넷에서 오든, ALB의 SG가 부착되지 않은 ENI에서 오는 트래픽은 게이트웨이의 ENI에 닿지 못합니다. 아웃바운드도 같은 결입니다. 게이트웨이는 자기 뒤에 붙어 있는 백엔드 서비스의 SG에만 호출을 보낼 수 있고, 그 외의 자리로는 트래픽이 나가지 않도록 묶여 있습니다.

 

이 매트릭스를 NACL과 함께 두면 결이 더 분명해집니다. NACL은 서브넷 단위에서 큰 자물쇠를 걸어 두는 자리이므로, "ALB의 IP 대역에서 오는 트래픽이 Private-App 서브넷에 들어와도 되는가"라는 큰 질문에 답합니다. SG는 그 안에서 다시 한 번 좁히는 자리이므로, "그 트래픽이 정확히 ALB의 SG에서 출발한 것인가"라는 정밀한 질문에 답합니다. 두 도구가 같은 결에서 서로 다른 굵기로 작동하는 자리라고 정리해 두면 좋겠습니다.

 


 

NACL이 막는 것 — SG가 막지 못하는 사고의 자리

이쯤에서 다시 처음의 질문으로 돌아옵니다. SG가 ENI 단위에서 정밀하게 동작한다면, NACL은 굳이 왜 두는가. 답은 SG가 막지 못하는 사고의 자리에 있다고 봤습니다. 가장 자주 떠올린 자리는 사설 데이터 서브넷의 아웃바운드 트래픽이었습니다.

 

이 프로젝트의 NACL 4세트 중 하나는 nacl-prod-private-data이고, 이 자리에는 데이터 서브넷의 ENI들이 묶여 있습니다. RDS, ElastiCache, MongoDB Atlas 호출의 종단이 자리 잡는 서브넷이며, 정상적인 운영에서 이 자리의 워크로드가 외부 인터넷으로 나갈 일이 없는 자리이기도 합니다. 그래서 이 NACL의 아웃바운드 룰은 0.0.0.0/0으로 향하는 트래픽을 명시적으로 차단하도록 그려 두었습니다.

이 결정이 왜 SG로 대체되지 않는가, 라는 질문에 답하려면 사고의 결을 한 번 가정해 보는 편이 좋겠습니다. 어떤 사고로 데이터 계층의 한 컨테이너가 SSRF나 RCE 같은 공격을 받았다고 생각해 봅니다. 공격자가 그 컨테이너를 손에 넣으면 컨테이너 안에서 임의의 명령을 실행할 수 있고, 외부 IP로 데이터를 흘려보내려는 시도를 할 수 있습니다. 컨테이너 안의 자격 증명이 같은 사고로 함께 유출될 수도 있습니다.

 

이 자리에서 SG는 ENI 단위로 동작하므로, 공격자가 새로운 SG 룰을 추가하거나 기존 SG의 룰을 느슨하게 수정하는 시도가 있을 수 있습니다. 만약 IAM 권한이 함께 새어 나간 사고라면 SG 자체가 변경될 수도 있는 자리이기도 합니다. NACL은 서브넷 단위의 자물쇠이고, 데이터 서브넷에서 외부 인터넷으로 나가는 길 자체를 닫아 두면, 그 안에서 어떤 SG가 어떻게 바뀌어도 트래픽이 서브넷의 경계를 넘지 못합니다. 자격 증명의 유출과 데이터의 유출이 같은 사건으로 묶이지 않도록 분리해 두는 자리라고 정리해 두면 결이 잘 맞습니다.

 

이 결은 직전 글(시리즈 004)에서 따라간 PrivateLink의 결과 같은 자리에 놓여 있습니다. PrivateLink가 트래픽을 AWS 백본 안에 가두어 데이터의 행선지를 통제하는 자리를 만들어 준다면, NACL은 서브넷의 외부 인터넷 출구 자체를 닫아 두는 자리에서 같은 결의 통제를 한 단계 더 두텁게 합니다. 어떤 한 도구의 결과로 데이터 유출 경로가 0이 되는 것이 아니라, 여러 도구가 합쳐졌을 때 비로소 그 속성이 성립한다는 점은 본 시리즈가 따라가는 척추 중 하나이기도 합니다.

 

이 자리에서 한 가지 더 짚어 두고 싶은 결은, 데이터 서브넷의 NACL 아웃바운드 차단이 정상 트래픽까지 함께 막지는 않는가 하는 질문이었습니다. 데이터 계층의 워크로드가 외부와 통신해야 하는 자리(예: 외부 SaaS 호출, 패키지 다운로드)가 있다면, 그 자리는 데이터 서브넷이 아닌 별도의 서브넷에서 처리하도록 설계 단계에서 분리해 두어야 한다는 점이 결정 회계의 한 줄로 따라옵니다. 데이터 서브넷의 책임을 "데이터의 종착지로만 동작한다"는 한 줄에 묶어 두면, NACL의 아웃바운드 차단이 정상 트래픽을 끊지 않는 자리에서 작동하게 됩니다. 한 도구의 결정이 다른 결정의 결을 좁힌다는 점에서, 서브넷 분리와 NACL의 아웃바운드 차단은 같은 결의 두 자리에 서 있다고 정리할 수 있습니다.

 



변경 관리 — 콘솔 한 자리에서 시작되는 사고를 막는 두 겹

SG와 NACL의 결정이 IaC 설계 단계에서 한 번 그려지고 나면, 그 뒤로 가장 자주 사고가 일어나는 자리는 의외로 운영 단계의 콘솔입니다. 누군가 급히 이슈를 풀기 위해 SG의 인바운드 룰 한 줄을 콘솔에서 수정하고, 그 변경이 IaC에 반영되지 않은 채로 잊혀지면, 다음 배포에서 IaC의 정의가 콘솔 변경을 덮어쓰면서 알 수 없는 시점에 룰이 사라지는 사고가 일어날 수 있습니다. 반대로 콘솔에서 너무 느슨하게 풀어 둔 룰이 IaC에 정의되어 있는 좁은 룰을 덮으면서 보안 경계가 의도와 다르게 흐려지기도 합니다.

 

이 자리에서 두 겹의 안전장치를 두는 일이 결정의 무게를 줄여 줍니다. 첫 겹은 Terraform을 단일 진실 공급원으로 두는 자리입니다. SG와 NACL의 모든 룰을 코드 한 자리에서 정의하고, 어떤 변경도 코드 리뷰와 PR 머지를 거치도록 흐름을 묶어 두면, 콘솔에서의 즉흥적인 변경이 표면 위로 드러납니다. 둘째 겹은 AWS Config의 매니지드 룰입니다. AWS 공식 문서가 안내하는 매니지드 룰 중 restricted-common-ports는 지정된 TCP 포트가 0.0.0.0/0(또는 ::/0)에서 직접 도달 가능한지 자동으로 검사해 줍니다. 공식 문서가 안내하는 기본 검사 대상은 FTP 데이터/명령(20, 21), MySQL(3306), RDP(3389), 그리고 환경에 따라 의미가 달라지는 4333번 포트이며, SSH(22)처럼 추가로 닫아 두고 싶은 포트는 blockedPorts 파라미터로 직접 지정해 두는 결로 다루게 됩니다. 코드 외 자리에서 일어난 변경이 이 룰의 평가 결과로 드러나는 자리이기도 합니다.

 

CIS AWS Foundations Benchmark가 SG의 통제를 권장 사항의 한 자리로 명시하고 있다는 점도 같은 결입니다. 이 벤치마크는 보안 설정의 베이스라인을 정리해 둔 권고이고, SG의 인바운드 룰에서 0.0.0.0/0이 일반 포트에 직접 도달하지 않도록 두는 결정이 그 베이스라인의 한 줄로 들어 있습니다. AWS Config의 매니지드 룰과 CIS 벤치마크가 같은 결로 작동하는 자리가, 콘솔 한 자리에서 시작되는 사고를 표면화해 주는 두 번째 안전장치라고 정리할 수 있을 것 같습니다.

 

CloudTrail은 그 위에 한 겹을 더해 줍니다. 어떤 IAM 보안 주체가 어떤 시점에 SG나 NACL의 룰을 변경했는지가 모두 기록으로 남고, 사고의 결정 회계가 사후에 추적 가능한 형태로 보존됩니다. Terraform이 미래의 결정을 선언적으로 박아 두는 자리라면, CloudTrail은 과거의 결정을 사실로 남겨 두는 자리이고, 두 자리가 합쳐졌을 때 변경 관리의 결이 비로소 단단해진다고 봤습니다.

 



두 개라 중복이 아니라 두 곳을 지킨다

이 글을 정리하면서 가장 크게 남은 인상은, SG와 NACL을 함께 두는 결정의 회계가 "두 개라 중복"이 아니라 "두 곳을 지킨다"는 결로 정리된다는 점이었습니다. 두 도구는 같은 트래픽을 두 번 검사하는 도구가 아니라, ENI와 서브넷이라는 두 자리에서 서로 다른 책임을 나눠 지는 도구이고, 한쪽이 우회되거나 잘못 설정되어도 다른 한쪽이 남는 자리에서 비로소 심층 방어가 됩니다.

 

스테이트풀과 스테이트리스의 차이는 운영의 결을 결정짓는 자리이고, SG ID 참조의 결은 컨테이너 워크로드의 결정 회계를 한 단계 추상화 위로 올려 둔 자리이며, NACL의 서브넷 단위 자물쇠는 SG가 막지 못하는 사고의 자리를 닫아 둡니다. 이 세 자리에서 두 도구의 책임이 따로 그려지고, 운영 단계에서는 그 분리된 책임이 누적된 안전장치로 작동합니다.

 

체크리스트로 정리해 두면 다음 다섯 자리는 새 VPC를 그릴 때 한 번씩 점검해 두는 편이 좋겠습니다. SG의 룰을 가능하면 SG ID로 참조하도록 그려 두었는가. 외부 인터넷으로 나갈 일이 없는 데이터 서브넷의 NACL 아웃바운드를 명시적으로 차단해 두었는가. SG와 NACL의 모든 변경이 IaC 한 자리에서 정의되고 있는가. AWS Config의 매니지드 룰로 보안에 민감한 포트의 노출을 자동 검사하고 있는가. CloudTrail로 SG와 NACL의 변경 이력이 사후에 추적 가능한 형태로 남고 있는가. 다섯 자리는 모두 운영 단계가 아닌 IaC 설계 단계에서 한 번 결정해 두면 그 뒤로는 거의 바뀌지 않는 자리이기도 합니다.

 

마지막으로 한 가지 결을 덧붙이자면, 본 시리즈의 글들이 함께 따라가는 결은 한 도구의 결과로 보안이나 가용성 같은 시스템 속성이 성립하지 않는다는 점입니다. PrivateLink가 트래픽을 백본 안에 가두고, NACL이 서브넷의 외부 출구를 닫고, SG가 ENI 단위에서 호출 그래프를 좁혀 두는 일이 한 자리에 모이면, "데이터 유출 경로 0"이라는 표현이 한 도구의 결과가 아니라 여러 도구의 결정이 합쳐진 결과로 비로소 성립합니다. 본 글이 그 누적 효과의 한 자리를 정리하는 작은 참고가 되었으면 좋겠습니다.

 



다음 글에서 이어서 다뤄 보고 싶은 것

본 시리즈의 다음 글에서는 본 글이 짚은 "두 도구의 책임 분리"가 실제 침해 시뮬레이션에서 어떻게 작동하는지를 한 단계 더 내려가 다뤄 볼 예정입니다. 한 컨테이너의 자격 증명이 유출되었다고 가정하고, 그 자격 증명으로 어떤 자리까지 도달할 수 있는지를 SG·NACL·PrivateLink가 함께 만들어 내는 경계 위에서 따라가 보려 합니다. "데이터 유출 경로 0"이라는 표현이 한 도구의 결과가 아니라 여러 도구가 합쳐진 결과라는 점이 다음 글의 척추가 될 것 같습니다.

이 글이 비슷한 자리에서 같은 종류의 회계를 해 보려는 분들께 작은 참고가 된다면 좋겠습니다.



참고한 공식 문서