이미지
책으로 공부하고 있는 내용에서는 VPC는 거의 언급되지 않았다. 그런데 회사 동료가 프로젝트용으로 구축한 AWS 인프라를 설명해줄때에는 VPC를 가장먼저 설명해주었고 꽤 오랜시간을 할애해서 이해시켜주었다. 그런데 왜 책에서는 크게 언급되지 않았을까? 나는 VPC를 처음 접했을때 Virtual PC의 약자로 생각했다. VPC가 네트워크라는 생각은 상상도 못했다.  회사 동료의 설명으로는 인프라를 구축할때 제일먼저 만드는게 VPC라고 했다. 그만큼 중요하다는건데 아무래도 네트워크 개념이다보니 일반 교재에서는 크게 다루지 않고 넘어간듯 하다. 하지만 실제 운영용 인프라에서는 네트워크는 보안과 직결되기때문에 꽤나 복잡하게 그리고 촘촘하게 구성하는거 같다. 어찌되었든 초보자인 내가 이해하기에는 다소 어려움이 있지만 쉽게 풀이하자면 “가상 네트워크”라고는 하는데, 네트워크 자체가 익숙하지 않으니까 여전히 어렵게 느껴지기는 했다. 그래서 내가 이해한 VPC를 내 말로 다시 정리하고, 서브넷/라우팅/보안까지 연결되는 흐름을 적어나가 보겠다. VPC 기본 개념: 내가 이해한 “AWS 안의 사내망(내 전용 네트워크)” AWS 제공. VPC VPC(Virtual Private Cloud)는 내가 이해하기로 “AWS 안에서 만드는 나만의 가상 사설 네트워크”다. 나는 처음엔 VPC를 VMWare의 가상PC정도로 그냥 설정 하나로 생각했는데, 실제로는 내 서버(EC2), DB(RDS), 로드밸런서(ALB) 같은 리소스들이 살아갈 ‘땅’이자 ‘사내망’이었다. 그래서 VPC를 한 번 제대로 잡아두면 이후 서비스들이 조금 덜 흩어져 보였다. 물리서버로 본다면 아마도 IDC에 서버를 장착하는 랙정도로 생각하면 될까? 맞는 비유인지는 모르겠지만 결국은 네트워크를 구성하는거라고 정리했다. VPC를 만들 때 가장 먼저 하는 건 IP 대역(CIDR)을 정하는 것이다. 예를 들어 10.0.0.0/16 같은 범위를 잡는다. 이 CIDR은 “내 사내망의 주소 체계” 같은 거라서, 나중에 확장...

ALB 실습 기록 (ALB 생성, SG 연결, 헬스체크)

이미지
AWS를 실제로 구성하고 연결하는 실습을 진행해 보았다. 전체적으로 개념이 약해서겠지만 실습을 진행하면서 AWS 네트워크가 어렵다고 느껴졌고, ALB를 “만든다”보다 “제대로 연결한다”가 더 어렵게 느껴졌다. 특히 ALB는 만들었는데 접속이 안 되면 보안그룹, 타겟그룹, 서브넷, 라우팅까지 다 의심해야 해서 머리가 하얘진다. 그래서 ALB 하나를 실제로 만들고, “ALB SG → EC2 SG” 연결 규칙을 어떻게 설정했는지 콘솔 화면 기준(캡처 순서)으로 학습 기록을 남긴다. 사전 준비: 내가 점검한 체크리스트 나는 실습 전에 “구조를 먼저 고정”하는 게 중요했다. ALB는 퍼블릭에서 요청을 받고, EC2는 프라이빗에 숨긴다는 기본 원칙을 세웠다. 그래서 내가 준비한 전제는 다음과 같다. 첫째, VPC는 이미 있고(없으면 새로 생성), Public Subnet 2개 + Private Subnet 2개로 AZ 2개에 걸쳐 준비되어 있어야 한다. ALB는 최소 2개 AZ에 걸쳐 서브넷을 선택하는 경우가 많고, 나도 고가용성 감각을 익히려고 2개를 기준으로 잡았다. Public Subnet 라우트 테이블에는 0.0.0.0/0 -> IGW가 있어야 한다. 여기서 나는 캡처를 하나 남겼다. “Public Subnet 선택 화면 + 해당 Subnet의 Route Table 화면”을 같이 찍어두면 나중에 ‘왜 퍼블릭이 아닌데?’를 바로 확인할 수 있다. 둘째, EC2는 Private Subnet에 배치해두고, 앱이 실제로 포트를 리슨하게 만들어둔다. 나는 여기서 실수를 자주 했는데, SG만 열어놓고 애플리케이션이 안 떠 있으면 헬스체크가 실패한다. 그래서 EC2에서 80이나 8080 중 하나를 정하고, 그 포트로 간단한 응답(예: /health에서 200)을 주도록 준비했다. 이 부분도 캡처 포인트다. “EC2 인스턴스 상세 화면(서브넷/보안그룹/프라이빗 IP) + 서버에서 리슨 포트 확인”을 같이 남겨두면 원인 추적이 쉬워진다. 셋째, NAT가 없으면 Pr...

AWS에서 ELB의 역할, 개념잡기 그리고 실습

이미지
처음 근무를 했던 회사의 서버는 2개의 WEB서버, 2개의 WAS서버 그리고 1개의 DB로 구성되어 있었다. 비교적 큰 회사였기때문에 인프라팀이 따로 있었고 개발자는 개발만 할 수 있는 환경이었다. 여하튼 웹서버가 2대인데 서버1대가 죽었을때 나머지 한대가 서비스를 지속적으로 할 수 있는 구조였는데 평상시에는 부하 분산을 어떻게 할까 궁금해서 PM에게 질문을 한 적이 있었다. 그때 WEB서버 앞에 로드밸런서가 있어서 적절하게 분산시키고 있어요라고 대답을 들었다. 아마도 로드밸러서라는것에대해서 처음 알게 되었다. 이렇게 예전에는 “로드밸런서 = 트래픽 나눠주는 것” 정도로만 알고 있었다. 그런데 AWS에서 ELB를 보니 ALB, NLB, (그리고 예전의 CLB)까지 종류가 나뉘어 있어서 더 헷갈렸다. 그래서 이번 기회에 AWS 공부를 하면서 정리한 ELB 개념을 정리해보려 한다. 목표는 “언제 ALB를 쓰고, 언제 NLB를 쓰는지”를 내 말로 설명할 수 있게 되는 것이다. 소제목 1 - ELB 기본: 내가 이해한 “트래픽 입구를 하나로 만드는 역할” ELB(Elastic Load Balancing)는 내가 이해하기로 “서비스 앞단에서 트래픽을 받아서 뒤쪽 자원으로 분산해주는 입구”다. 사용자는 내 서비스의 실제 서버(EC2)가 몇 대인지, 어디에 있는지 몰라도 된다. 사용자는 ELB로 들어오고, ELB가 뒤에서 살아있는 서버로 요청을 넘겨준다. 그래서 나는 ELB를 “문지기 + 교통정리”라고 생각했다. 처음엔 로드밸런서를 굳이 왜 써야 하나 싶었는데, 공부하면서 가장 크게 와닿은 건 고가용성과 확장성이다. 서버 한 대가 죽어도 ELB가 다른 서버로 보내주면 서비스가 계속 살아있다. 트래픽이 늘면 서버를 더 붙이고(오토스케일링), ELB는 새로 붙은 서버로도 자동으로 분산한다. 또한 보안 측면에서도 장점이 있다. 외부에 직접 노출되는 건 ELB만 두고, 실제 앱 서버는 Private Subnet에 숨기는 구조가 가능해진다. 나는 “Public ALB + Pr...

AWS 방화벽 보안그룹 이해하기

이미지
모바일개발을 오래하다보니 생각보다 인프라 지식이 부족한편이다. 인프라 지식이 부족해서 AWS에 대해서 공부하면서 '보안그룹'에 대해서 읽어볼때 단순하게 '방화벽'이구나 하고 이해하고 넘어갔다. 예전에 회사서버가 해킹당해서 '코인'을 열씸히 채굴한적이 있었는데 해킹당한 사실을 발견하기 전에는 운영중인 서버가 항상 문제가 없다고 생각했다. 하지만 보안 컨설팅을 받고나서 하루에서 수백번 이상 해킹시도가 있었던 로그기록을 확인하고나서야 서버는 항상 외부의 침입에 노출되어 있다는걸 알게 되었다. 각설하고 '보안그룹'은 방화벽이 맞는것같다. 몇일전에 AWS 인프라를 담당하는 회사 직원에서 '보안그룹'에 대해서 질문을 했는데 '방화벽'이 맞다고 했다. 그리고 우리회사 인프라는 모두 private으로 구성되어 있고 모든 인바운드, 아웃바운드는 보안그룹에 의해서 관리되고 있다고 말했다. 조금 어렵기는 했지만 방화벽으로 모둔 입출력이 관리되고 있다고 이해했다. 그만큼 중요한 거겠지 생각했다.  중요한거니 조금더 힘을내고, 공부하면서 정리한 AWS 보안그룹 개념을 학습 기록 형태로 남긴다. 보안그룹 기본: 내가 이해한 “인스턴스 단위 상태기반 방화벽” 내가 처음 정리한 한 문장은 이거다. “보안그룹은 EC2(정확히는 ENI)에 붙는 상태 기반(Stateful) 방화벽이다.” 여기서 ‘상태 기반’이라는 말이 처음엔 어려웠는데, 나는 이렇게 이해했다. 인바운드로 들어오는 트래픽을 허용하면, 그 응답 트래픽은 자동으로 허용된다. 반대로 아웃바운드로 나가는 트래픽을 허용하면, 그 응답도 자동으로 돌아올 수 있다. 즉, 들어오고 나가는 왕복을 사람이 일일이 다 뚫어주지 않아도 되는 구조라서, 초보자인 내가 만지기엔 NACL보다 SG가 덜 무섭게 느껴졌다. 그리고 보안그룹은 ‘허용(Allow) 규칙만 있다’는 점도 중요했다. 나는 처음에 “막는 규칙(Deny)도 있겠지”라고 생각했는데, SG는 기본적...

Public ALB + Private EC2 + RDS 구조의 기본 아키텍처 구상하기

이미지
예전에 일했던 회사의 서버구조가 2중화로 구성된 Web + WAS + DB구조였다. 우리 회사의 인프라 담당자에게 물어보니 연습하기에 좋은 구조로 “Public ALB + Private EC2 + RDS”를 추천해줬는데 예전에 담당했던 서비스의 구조와 비슷하다는걸 느꼈다. 그 전에는 잘 알지도 못하면서 AWS 아키텍처 그림을 보면 늘 “대충 이런 느낌이겠지”로 넘겨버리곤 했다. 그래서 2026년 기준으로 가장 흔한 형태이자 연습하기 좋다는 “Public ALB + Private EC2 + RDS” 기본 아키텍처를 머리속에서 정리하고, VPC/라우팅/보안 흐름이 머릿속에서 연결되도록 학습 기록으로 남긴다. 소제목 1 - VPC와 서브넷 설계: 내가 머릿속에 먼저 그린 지도 이 아키텍처를 이해하려면 “리소스를 어디에 둘 것인가”부터 결정해야 했다. 나는 항상 서비스부터 만들고 네트워크는 나중에 맞추려다가 더 헷갈렸는데, 이번엔 반대로 VPC 지도를 먼저 그렸다. 내가 잡은 기본은 VPC 하나(예: 10.0.0.0/16) 안에 서브넷을 최소 4개로 나누는 구성이다. 이유는 고가용성을 위해 AZ를 2개 쓰기 위해서다. 그래서 Public Subnet 2개(AZ A, AZ B), Private Subnet 2개(AZ A, AZ B)로 시작하는 그림을 머리에 넣었다. 퍼블릭/프라이빗은 ‘이름표’가 아니라 라우팅이 결정된다는 것도 이미 여러 번 헷갈려봤기 때문에, 서브넷을 만들고 나서 라우트 테이블을 꼭 같이 떠올리기로 했다. Public Subnet에는 인터넷과 맞닿아야 하는 리소스가 들어간다. 여기서는 ALB가 대표다. ALB는 외부 사용자의 요청을 받아야 하니까 퍼블릭에 있어야 하고, 보통 가용영역 2개에 걸쳐서 배치된다. 그리고 Private Subnet에는 EC2(앱 서버)와 RDS(DB)를 두는 게 기본이다. 특히 RDS는 인터넷에 노출되면 안 되기 때문에, 퍼블릭에 두지 않는다는 원칙을 내 머리에 박아두었다. 여기서 내가 한 번 더 정리한 포인트는 “...

AWS 안의 중요한 용어 개념잡고 가기 1

이미지
예전에 VMWare를 이용해서 3개의 서버를 구성하고 네트워크 실습을 한적이 있었다. 하지만 곧 흐지부지되고 공부를 마칠수밖에 없었는데 당시 모바일개발을하던 나에게 네트워크는 너무 먼 이야기였던거 같다. 지금도 마찬가지로 AWS를 공부할 때 제일 막막했던 파트가 네트워크였다. 그래서 2026년 기준으로 내가 이해한 범위 안에서 VPC부터 서브넷, 라우팅, 보안(SG/NACL)까지 핵심 용어를 내 말로 정리해 학습 기록으로 남긴다. 물론 회사 동료에게 속성으로 배우는 시간이 있었는데 솔직히 너무 어려운 인터페이스때문에 어느 메뉴가 어디인지도 지금은 기억이 나지 않는다. 그래서 천천히 나중에 헷갈릴 때 다시 읽을 수 있게 “한 줄 정의 + 실무 감각” 중심으로 적어보려고 한다. 소제목 1 - VPC: AWS 안에서 만드는 나만의 가상 사설 네트워크 VPC는 내가 AWS에서 처음 제대로 붙잡은 개념이다. 예전에는 “그냥 네트워크 설정 같은 거겠지”라고 생각했는데, 실습을 해보니 VPC는 거의 모든 리소스 배치의 시작점이었다. 내가 이해한 VPC는 “AWS 안에서 만드는 나만의 가상 사설 네트워크, 즉 데이터센터 한 칸”이다. 회사에서 사내망을 따로 운영하듯이, AWS에서도 내 서비스가 돌아갈 사내망을 하나 만든다고 보면 감이 잡혔다. VPC를 만들 때 제일 먼저 하는 건 IP 대역(CIDR)을 정하는 것이다. 예를 들어 10.0.0.0/16 같은 범위를 잡아두고, 그 안에 EC2(서버), RDS(DB), ALB(로드밸런서) 같은 자원을 배치한다. 여기서 중요한 건 “처음부터 너무 작게 잡지 말 것”과 “겹치지 않게 잡을 것”이라고 들었다. 나중에 다른 네트워크랑 연결(VPN/Peering/Transit Gateway 같은 것)을 할 수도 있는데, CIDR이 겹치면 골치가 아프다고 해서 나는 공부용이라도 신중하게 잡아두려고 했다. VPC를 ‘한 덩어리’로만 보면 좀 추상적인데, 내 머릿속에서는 “큰 땅”으로 생각하니 이해가 쉬웠다. 땅이 있으면 구역을 나눠야 하...

AWS 계정만들기 도전

이미지
프론트 기반 개발자라 겪었던 인프라에 대한 공포를 극복하기 위해서 그리고 인프라 지식이 부족한 편이고, 막막한 마음을 없애기 위해서 AWS 공부에 도전했다. 그런데 시작부터 AWS는 “잘못 만지면 비용 폭탄”이라는 얘기를 많이 들어서 가입부터 겁이 났다. 그래서 2026년 기준으로 AWS 프리티어 계정을 만들기 전에 내가 준비한 것(이메일, 결제, 보안)을 학습 기록처럼 정리해보았다.  1 - 이메일 준비: 계정 만들기 전에 내가 체크한 것들 AWS 프리티어를 시작하기로 마음먹고 제일 먼저 한 건, 로그인 이메일부터 분리하는 일이었다. 예전에는 어떤 서비스든 그냥 개인 메일 하나로 가입해버렸는데, AWS는 결제와 보안이 엮이니까 그렇게 하면 나중에 관리가 힘들 것 같았다. 특히 나는 실습하다가 계정 설정을 꼬일 가능성이 높다고 생각해서, “AWS 전용 이메일”을 새로 만드는 쪽으로 갔다. 내가 체크한 포인트는 단순했다. 첫째, 이 이메일은 비밀번호를 강하게 하고 2단계 인증까지 걸어둘 것. AWS 자체 보안도 중요하지만, 이메일이 뚫리면 계정 복구 과정이 흔들릴 수 있으니 결국 이메일이 핵심이었다. 둘째, 메일함에서 AWS 관련 메일이 묻히지 않게 라벨/필터를 만들었다. 가입 확인, 결제 알림, 보안 경고 같은 메일을 놓치면 이후에 “왜 과금됐지?” 같은 상황이 생길 수 있을거라 생각했다. 또 하나는 계정 이름(어카운트 네이밍)이다. 나는 나중에 포트폴리오나 실습 계정을 여러 개 운영할 수도 있을 것 같아서, 이메일과 별개로 계정의 용도를 이름에 남기는 습관을 들이기로 했다. 예를 들면 “study-free-tier” 같은 식으로 목적을 적어두면, 콘솔이나 청구서에서 볼 때도 헷갈리지 않을 것 같았다. 그리고 마지막으로 내가 미리 마음먹은 건, “가입 후 바로 루트 계정을 거의 안 쓸 것”이다. 이게 이메일 준비와 연결되는 이유가 있다. AWS 가입은 루트 계정으로 시작되지만, 이후에 IAM 사용자로 넘어가야 안전하다는 말을 많이 봤다. 그래서...

비전공자의 AWS 도전기

이미지
개발일을 하면서 웹개발과 안드로이드 그리고 프론트의 javascript까지 거쳐오면서 나는 비전공자로 개발 일을 시작했지만, 끊임없이 공부하면서 달려온거 같다. 그런데 개발자가 유독 약한 인프라와 AWS 지식이 나역시 많이 부족해서 매번 배포나 장애 이슈가 나오면 겁부터 났다. 그래서 2026년 기준으로 AWS를 “기초→실습→자격증” 흐름으로 공부하면서, 내가 실제로 무엇을 이해했고 어디서 막혔는지까지 학습 기록 형태로 정리해보려 한다. 마침 회사에 AWS를 잘하는 직원까지 새로 들어와서 어느정도 환경이 만들어져서 희망이 보이기 시작했다. AWS 기초: 비전공자인 내가 먼저 잡아야 했던 개념 처음 AWS를 켰을 때 가장 당황했던 건 서비스가 너무 많다는 점이었다. EC2, S3, VPC, IAM… 이름은 많이 들었는데, 막상 “이걸 왜 쓰는지”가 연결이 안 됐다. 그래서 나는 외우는 걸 멈추고, 가장 먼저 큰 그림부터 잡기로 했다. 결론부터 말하면, 내게 가장 도움이 됐던 기초는 리전/가용영역, 네트워크(VPC), 권한(IAM), 비용 구조 이 네 가지였다. 이 네 가지를 잡고 나니, 이후 서비스들이 조금씩 논리적으로 보이기 시작했다. 우선 리전(Region)과 가용영역(AZ)은 “장애를 어떻게 피하는가”를 설명하는 단어였다. 예전엔 그냥 AWS 지역 선택 정도로만 이해했는데, 공부하면서 깨달았다. 한 리전 안에도 여러 AZ가 있고, 같은 AZ에만 모든 걸 몰아넣으면 특정 장애에 같이 쓰러질 수 있다는 것. 그래서 실무에서 “멀티 AZ”라는 말이 괜히 나오는 게 아니었다. 나는 아직 설계를 깊게 하진 못하지만, 최소한 “하나만 만들지 말고 분산할 수 있나?”라는 질문을 스스로 던지게 됐다. 그다음은 VPC였다. 여기서부터 나는 자주 멘붕이 왔다. 퍼블릭 서브넷, 프라이빗 서브넷, 라우팅 테이블, 인터넷 게이트웨이 같은 말이 한꺼번에 튀어나오는데, 처음엔 다 같은 네트워크 설정처럼 느껴졌다. 그래서 나는 이렇게 정리했다. “인터넷에 공개될 공간과 내부 전...

EXPLAIN 기초: 점검/리포트 쿼리가 느려질 때 “왜 느린지” 확인하는 방법

포인트 시스템을 만들어가면서 새삼 느끼는것은 생성형AI가 있어서 설계하고 구상하는게 너무 쉬워졌다는것입니다. 혼자서 했다면 아마도 어려움이 많았을거 같은데 AI와 대화를 하며 개선점을 찾아가는 방식으로 진행하다보니 생각보다 기능이 치밀하게 설계를 할 수 있었습니다. 지금까지 하나하나 학습범위를 늘려가며 왔는데 아직도 갈길이 먼거 같습니다.  포인트 원장(point_history)은 시간이 지날수록 커집니다. 데이터가 커지면, 기능 쿼리(내역/잔액)뿐 아니라 점검 쿼리(원장 vs balance 비교, 월별 집계, ref_id 그룹핑)도 점점 느려집니다. 이때 흔히 “인덱스를 더 만들면 되지 않을까?”라고 생각하지만, 인덱스는 무조건 늘린다고 좋아지지 않습니다. 어떤 쿼리가 어떤 방식으로 실행되는지 모르면 잘못된 인덱스를 만들거나, 효과 없는 튜닝을 반복할 가능성이 큽니다. 그래서 필요한 도구가 EXPLAIN 입니다. EXPLAIN은 “이 쿼리를 DB가 어떤 순서로 실행할지”를 보여주는 실행 계획입니다. 즉, 느린 이유를 추측하지 않고 눈으로 확인 할 수 있게 해줍니다. 이 단원의 목적: 실행 계획을 보고 “어디를 고칠지” 결정하기 이번 글의 목표는 아래 3가지입니다. EXPLAIN 결과에서 최소한의 핵심 항목을 읽을 수 있게 된다 포인트 시스템 쿼리에서 자주 나오는 느린 패턴(풀 스캔, filesort)을 구분한다 인덱스를 “조회 패턴에 맞게” 설계하는 감각을 만든다 1) EXPLAIN 사용법: SELECT 앞에 붙이면 된다 가장 기본은 SELECT 앞에 EXPLAIN을 붙이는 것입니다. EXPLAIN SELECT id, action_type, amount, created_at FROM point_history WHERE uid = '11111111-1111-1111-1111-111111111111' AND hide = 0 ORDER BY created_at DESC, id DESC LIMIT 50; 실행 결과는 ...

점검 SQL: “원장 합계(SUM) vs balance” 불일치를 찾아내고 원인을 좁히는 방법

포인트 시스템은 “정상 처리”만 잘 만들면 끝나는 것처럼 보이지만, 실제로는 시간이 지날수록 다양한 이유로 데이터가 어긋날 수 있습니다. 예를 들어 이런 상황이 생깁니다. 중간 장애로 트랜잭션이 일부만 반영된 것처럼 보이는 케이스(재처리/수동 개입 포함) 운영 정정(ADJUST)이나 소프트 삭제(hide) 처리 기준이 흔들린 케이스 배치 작업(만료/정산)이 예상과 다른 시간에 들어간 케이스 코드 변경 이후 특정 action_type이 누락/중복 집계된 케이스 그래서 “점검 쿼리”는 기능 쿼리만큼 중요합니다. 특히 balance 테이블(잔액 캐시)을 함께 쓰는 구조라면, 주기적으로 원장 합계와 balance가 같은지 확인해야 합니다. 이 단원의 목적: 불일치를 “발견”하고 “원인”을 좁힐 수 있는 쿼리 세트 만들기 이번 글에서는 아래 3가지를 목표로 합니다. 원장 합계(SUM)와 balance를 비교해 불일치 UID를 뽑는다 불일치가 발견되면 어디서부터 어긋났는지 범위를 좁힌다 자주 발생하는 유형(숨김 처리, 중복 요청, 배치 만료)을 점검 항목으로 만든다 1) 1차 점검: uid별 “원장 잔액”과 “balance 잔액” 비교 가장 기본은 uid별로 두 값을 나란히 놓고 비교하는 것입니다. 여기서는 hide=0만 원장 합계에 포함한다고 가정합니다. SELECT b.uid, b.balance AS balance_cache, COALESCE(l.ledger_balance, 0) AS balance_ledger, b.balance - COALESCE(l.ledger_balance, 0) AS diff FROM point_balance b LEFT JOIN ( SELECT uid, SUM(amount) AS ledger_balance FROM point_history WHERE hide = 0 GROUP BY uid ) l ON l.uid = b.uid WHERE b.balance <> COALESCE(l.ledger_...

저장 프로시저 기초: “포인트 사용”을 DB에서 하나의 호출로 처리하기

지금까지는 애플리케이션이 여러 SQL을 순서대로 실행한다는 관점으로 진행했습니다. 그런데 포인트 사용처럼 “항상 같은 흐름”을 따르는 작업은, DB에 로직을 저장해 하나의 호출로 실행 하는 방식도 많이 사용합니다. 이때 등장하는 것이 저장 프로시저(Stored Procedure) 입니다. 저장 프로시저는 “자주 쓰는 SQL 묶음”을 DB 안에 저장해두고, 필요할 때 CALL 로 호출하는 형태입니다. 장점은 흐름이 한 곳에 고정된다는 점이고, 단점은 로직이 DB에 묶이면서 운영/배포/테스트 전략이 필요해진다는 점입니다. 이 단원의 목적: 핵심 작업(포인트 사용)을 “단일 진입점”으로 만들기 포인트 사용은 멱등성/동시성/원장 기록/상태 관리가 같이 얽히는 대표 작업입니다. 프로시저로 만들면 다음 같은 효과를 기대할 수 있습니다. 처리 흐름(선점 → 차감 → 기록 → 완료)이 한 곳에 모인다 누가 호출하든(웹/앱/배치) 같은 규칙으로 처리된다 실수로 순서를 바꾸거나 일부 단계를 누락할 위험이 줄어든다 이번 글에서는 “요청 ID 선점 테이블(point_requests)”과 “잔액 테이블(point_balance)”을 사용하는 흐름을 기반으로, 포인트 사용 프로시저를 구성하는 예시를 보여줍니다. 1) 준비: 필요한 테이블/제약조건 요약 프로시저 예시는 아래 전제를 둡니다. point_requests : (uid, request_id) UNIQUE로 중복 요청 차단 point_balance : uid PK, balance 컬럼에 현재 잔액 저장 point_history : 원장 기록, hide=0 기본 그리고 포인트 사용은 “조건부 UPDATE(잔액 충분할 때만 차감)” 패턴을 사용합니다. 2) 프로시저 설계: 입력값과 출력값을 먼저 정한다 프로시저는 “인자”로 입력을 받고, 필요하면 결과를 출력합니다. 포인트 사용 프로시저라면 최소 입력은 다음이 필요합니다. p_uid: 사용자 p_request_id: 멱등 키(중복 ...

VIEW 기초: 복잡한 리포트 쿼리를 “가상 테이블”로 고정해 재사용하기

포인트 시스템을 만들다 보면 쿼리가 점점 길어집니다. 회원별 잔액, 최근 30일 적립/사용, 최근 활동 시각, 이상 징후용 집계까지 한 번에 뽑으려면 JOIN과 서브쿼리가 늘어나고, 같은 로직이 여러 곳에서 반복되기 시작합니다. 이때 도움이 되는 도구가 VIEW(뷰) 입니다. VIEW는 데이터를 저장하는 테이블이 아니라, 특정 SELECT 쿼리에 이름을 붙여 “가상 테이블처럼” 쓰는 기능 입니다. 즉, “자주 쓰는 쿼리 조합을 한 번 정의해두고, 이후에는 SELECT로 간단히 가져오는 방식”입니다. 이 단원의 목적: “반복되는 리포트 쿼리”를 한 곳에서 관리하기 VIEW를 도입하는 이유는 속도를 올리기 위해서가 아니라, 복잡도를 낮추기 위해서 인 경우가 많습니다. 이번 글에서는 아래 3가지를 목표로 합니다. VIEW가 무엇이고, 테이블과 무엇이 다른지 이해한다 포인트 시스템에서 자주 쓰는 “회원별 요약” VIEW를 만든다 VIEW를 쓸 때의 주의점(성능, 권한, 업데이트 가능 여부)을 정리한다 1) VIEW는 “저장된 SELECT”다 VIEW는 실행 결과를 저장하는 게 아니라, 정의된 SELECT를 매번 실행해서 결과를 보여줍니다(일반적인 VIEW 기준). 그래서 VIEW는 아래처럼 생각하면 이해가 쉽습니다. 테이블: 데이터가 실제로 저장됨 뷰: SQL 정의가 저장됨(결과는 요청할 때 계산) 즉, VIEW를 만든다고 해서 데이터가 복제되거나 늘어나지 않습니다. 대신 “이 SELECT를 앞으로는 view_name이라는 이름으로 부르겠다”는 약속을 만들어주는 것입니다. 2) 첫 번째 VIEW: 사용자별 현재 잔액(원장 기반) 가장 기본은 잔액 뷰입니다. point_history 원장을 uid별로 SUM해서 “현재 잔액”처럼 보이게 만들 수 있습니다. hide=1(숨김)은 제외한다고 가정합니다. CREATE OR REPLACE VIEW v_user_balance AS SELECT uid, SUM(amount) AS bala...

멱등성 + 동시성 결합: “요청 ID 선점”으로 중복 차감까지 한 번에 막기

동시성(잠금)만 잘 잡아도 잔액이 마이너스로 내려가는 문제는 많이 줄어듭니다. 그런데 운영에서 더 자주 만나는 현실은 “동시에 두 번”이 아니라 같은 요청이 재전송되어 여러 번 들어오는 것 입니다. 네트워크 타임아웃, 버튼 중복 클릭, 서버 재시도 로직은 생각보다 흔합니다. 그래서 포인트 사용 처리에서 정말 중요한 것은 아래 두 가지를 동시에 만족시키는 것입니다. 동시에 여러 요청이 들어와도 잔액이 깨지지 않는다(동시성) 같은 요청이 여러 번 들어와도 결과는 한 번만 반영된다(멱등성) 이번 글에서는 이 둘을 같이 해결하는 대표 패턴인 “요청 ID 선점(Reservation)” 전략을 다룹니다. 핵심은 간단합니다. “먼저 이 요청을 내가 처리하겠다고 기록하고(중복 차단), 그 다음 실제 차감을 진행한다.” 이 단원의 목적: “중복 요청”을 DB가 구조적으로 거절하게 만들기 멱등성은 코드로도 구현할 수 있지만, 서비스가 커질수록 여러 서버/여러 경로에서 동일 요청이 들어오며 코드만으로 일관성을 유지하기가 어려워집니다. 그래서 데이터베이스에 “중복을 거절하는 규칙(UNIQUE)”을 두고, 그 규칙을 활용해 처리 흐름을 설계하는 것이 강력합니다. 이번 글의 목표는 아래 3가지입니다. 요청 ID를 어디에 저장하고 어떤 UNIQUE를 걸어야 하는지 정한다 요청을 먼저 선점한 뒤 잔액 차감을 진행하는 트랜잭션 흐름을 만든다 실패/재시도 상황에서 “어떤 상태가 남는지”를 명확히 이해한다 1) 요청 ID 선점용 테이블 만들기 point_history는 원장이고, 요청 선점은 “처리 상태”를 관리하는 성격이 강합니다. 그래서 보통 별도 테이블을 둡니다. 이 테이블은 “요청이 들어왔는지”, “처리되었는지”, “실패했는지”를 추적할 수 있어야 합니다. CREATE TABLE IF NOT EXISTS point_requests ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, uid CHAR(36) NOT...

동시성(잠금) 기초: 동시에 차감 요청이 들어와도 잔액이 깨지지 않게 만드는 패턴

트랜잭션을 적용했다고 해서 모든 문제가 끝나지는 않습니다. 트랜잭션은 “중간 실패”를 막아주지만, 동시에 들어오는 요청(동시성) 까지 자동으로 해결해주지는 않습니다. 포인트 시스템에서 동시성이 터지는 대표 장면은 이렇습니다. 한 사용자가 거의 동시에 두 번 결제를 눌렀거나, 모바일/웹이 동시에 요청을 보내거나, 네트워크 재시도 때문에 같은 차감 요청이 겹쳐 들어옵니다. 이때 방어가 없으면 아래 같은 일이 생길 수 있습니다. 잔액이 충분하다고 판단한 두 요청이 동시에 차감을 진행해 잔액이 마이너스 가 된다 중복 차감이 발생한다(멱등성 문제와 결합) 원장과 balance 테이블이 순간적으로 어긋난다 이번 글에서는 “잠금(락)”을 어렵게 설명하기보다, 포인트 시스템에서 가장 현실적으로 쓰이는 동시성 방어 패턴을 중심으로 정리합니다. 이 단원의 목적: “읽고 판단 → 쓰기” 사이의 틈을 없애기 동시성 문제는 대부분 이 틈에서 발생합니다. 현재 잔액을 읽는다(충분하네) 차감한다 두 요청이 동시에 1번을 수행하면 둘 다 “충분하다”고 판단할 수 있습니다. 그래서 중요한 것은 1번과 2번 사이를 안전하게 만드는 것입니다. 이번 글의 목표는 아래 3가지입니다. 포인트 시스템에서 동시성 문제가 발생하는 구조를 이해한다 대표 잠금 패턴 2가지(행 잠금, 조건부 UPDATE)를 익힌다 원장(point_history)과 잔액(point_balance)을 함께 쓰는 경우의 안전한 흐름을 만든다 전제: 잔액을 어디에 두는가(원장만 vs balance 테이블) 원장만 쓰는 구조(매번 SUM으로 잔액 계산)는 개념적으로 단순하지만, “차감 가능 여부 확인”이 매번 무겁고, 동시성 제어도 구현 방식이 복잡해질 수 있습니다. 그래서 실무에서는 잔액을 별도 테이블(point_balance)로 캐시하고, 원장은 그대로 기록하는 “원장 + 잔액 캐시” 조합을 많이 씁니다. 이 글은 그 구조를 기준으로 설명합니다. -- 예시: 잔액 테이블 C...

트랜잭션 기초: 적립/사용 처리 중 ‘중간 실패’가 있어도 데이터가 깨지지 않게 하기

지금까지는 “SQL이 정상적으로 실행된다”는 전제에서 원장과 조회를 만들었습니다. 하지만 실제 시스템은 언제든 실패할 수 있습니다. 네트워크가 끊길 수도 있고, 디스크가 꽉 찰 수도 있고, 잠깐의 락 경합으로 타임아웃이 날 수도 있습니다. 포인트처럼 숫자에 민감한 도메인에서 가장 위험한 상황은 여러 작업 중 일부만 성공하는 것 입니다. 예를 들어 다음 같은 경우는 운영에서 큰 문제로 이어집니다. 포인트 사용(차감) 원장은 기록됐는데, 주문 상태 업데이트가 실패했다 결제는 성공했는데, 포인트 적립 원장 기록이 실패했다 balance 테이블을 함께 쓰는데, 원장만 기록되고 balance 갱신이 실패했다 이런 “반쯤 성공”을 막기 위한 도구가 트랜잭션(Transaction) 입니다. 트랜잭션은 여러 SQL을 하나의 묶음으로 실행해서, 전부 성공하면 확정(COMMIT), 하나라도 실패하면 전부 되돌림(ROLLBACK)으로 처리합니다. 이 단원의 목적: 포인트 처리의 “원자성(Atomicity)” 확보 트랜잭션을 이해할 때 핵심은 “문법”이 아니라 “보장되는 성질”입니다. 이번 글의 목표는 아래 3가지입니다. 트랜잭션이 어떤 문제를 해결하는지(중간 실패, 일관성 깨짐)를 이해한다 포인트 적립/사용에서 트랜잭션을 어떻게 적용하는지 기본 패턴을 익힌다 balance 테이블을 함께 쓸 때 반드시 지켜야 할 트랜잭션 규칙을 정리한다 1) 트랜잭션의 핵심 개념: “전부 또는 전무(All or Nothing)” 트랜잭션은 여러 SQL을 하나의 작업처럼 다룹니다. 아래처럼 생각하면 이해가 빠릅니다. COMMIT: 지금까지의 변경을 “확정”한다 ROLLBACK: 지금까지의 변경을 “되돌린다” BEGIN/START TRANSACTION: 변경 묶음을 시작한다 가장 단순한 형태의 흐름은 이렇습니다. START TRANSACTION; -- 여러 작업 수행 -- 1) 포인트 원장 기록 -- 2) (필요 시) balance 갱신 -- 3) ...

서브쿼리 vs JOIN: “최근 30일 적립 합계”를 가장 깔끔하게 구하는 기준

SQL을 배우다 보면 같은 결과를 만드는 방법이 여러 개라는 사실을 자주 만나게 됩니다. 특히 “서브쿼리로도 되는데 JOIN으로도 된다”는 상황이 흔합니다. 초반에는 둘 다 동작하면 그걸로 끝내기 쉽지만, 데이터가 커지고 요구사항이 늘어나면 “읽기 쉬운 쿼리”, “수정하기 쉬운 쿼리”, “오답이 덜 나는 쿼리”의 차이가 크게 드러납니다. 이번 글에서는 포인트 시스템에서 정말 자주 나오는 질문 하나로 비교해봅니다. “최근 30일 동안의 적립 합계(earned_30d)를 사용자별로 구하라.” 이 한 문장에 조건, 집계, 그룹핑, 결측 처리까지 다 들어있어서 연습 주제로 적당합니다. 이 단원의 목적: “정답을 만드는 방법”보다 “선택 기준”을 갖기 서브쿼리와 JOIN의 선택은 취향 문제가 아니라, 유지보수성과 안정성의 문제입니다. 이번 글의 목표는 아래 3가지입니다. 서브쿼리/파생 테이블(derived table)이 언제 읽기 쉬운지 이해한다 JOIN으로 풀었을 때 발생하기 쉬운 중복 집계 위험을 피하는 방법을 익힌다 동일 결과를 여러 방식으로 작성해 보고, 변경에 강한 형태를 고른다 0) 기준 정의: “적립(earned)”은 무엇인가 “적립 합계”라고 했을 때, 어떤 action_type을 적립으로 볼지는 정책입니다. 이 글에서는 다음을 적립 계열로 가정합니다. CHARGE: 일반 적립 USE_CANCEL: 사용 취소로 되돌림(결과적으로 +) ADJUST: 운영 정정(양수/음수 모두 가능하지만, 여기서는 ‘적립’ 합계에 포함 여부를 명확히 해야 함) 여기서 ADJUST는 정책에 따라 “적립으로 분류하지 않고 별도 집계”로 두는 경우도 많습니다. 다만 학습을 위해 “적립 계열로 묶되, 조건으로 쉽게 분리할 수 있다”는 방향으로 진행합니다. 1) 가장 단순한 답: point_history만으로 uid별 earned_30d 구하기 users 테이블을 굳이 붙일 필요가 없다면, point_history만으로도 답은 나옵니다. ...

JOIN 기초: users와 point_history를 합쳐 ‘회원별 요약(잔액/최근 활동)’ 만들기

포인트 원장(point_history)만으로도 잔액과 통계를 만들 수 있지만, 실제 화면이나 운영 도구에서는 거의 항상 “사용자 정보”가 함께 필요합니다. 예를 들어 이런 질문들은 point_history만으로는 답이 부족합니다. 이 uid가 누구인지(가입일, 상태 등) 함께 보고 싶다 회원 목록을 보면서 각 회원의 잔액/최근 활동을 한 줄로 요약하고 싶다 최근 7일 동안 사용이 많은 회원을 “회원 정보와 함께” 추려 보고 싶다 이때 필요한 것이 JOIN입니다. JOIN은 테이블을 “합치는” 기능이지만, 정확히는 두 테이블의 행을 연결 해 새로운 결과를 만드는 방식입니다. 이번 글에서는 포인트 시스템에서 가장 흔한 JOIN 패턴(회원 + 원장 요약)을 중심으로, INNER JOIN/LEFT JOIN의 차이와 실전에서 헷갈리는 포인트를 정리합니다. 이 단원의 목적: “회원 목록 + 포인트 요약”을 안정적으로 뽑아내기 JOIN은 문법을 외우는 것보다 “어떤 결과를 만들고 싶은지”를 먼저 그리는 것이 중요합니다. 이번 글의 목표는 아래 3가지입니다. INNER JOIN과 LEFT JOIN의 차이를 이해하고 상황에 맞게 선택한다 집계(GROUP BY)와 JOIN을 결합해 ‘회원별 잔액/통계’를 만든다 JOIN에서 자주 발생하는 오류(행 폭증, 중복 집계)를 피하는 패턴을 익힌다 준비: users에 최소한의 컬럼을 더해 “JOIN 결과가 읽히게” 만들기 이전 글에서는 users가 uid와 created_at만 있었지만, JOIN 결과를 보기 편하게 하려면 display_name 같은 컬럼이 하나 있으면 좋습니다. 실제 서비스라면 이메일/닉네임/상태값이 있을 텐데, 여기서는 학습 목적상 간단히 display_name을 추가합니다. -- 이미 있다면 생략 ALTER TABLE users ADD COLUMN display_name VARCHAR(50) NULL; -- 예시 값 입력(학습용) UPDATE users SET display_n...

GROUP BY/HAVING: “이상 징후”를 집계로 찾아내는 방법

SUM/COUNT로 리포트를 만들 수 있게 되면 다음 단계로 자연스럽게 넘어갑니다. “숫자를 보는 것”에서 끝나지 않고, 이상한 패턴을 자동으로 걸러내는 것 입니다. 포인트 시스템에서는 특히 아래 같은 상황이 자주 문제로 이어집니다. 특정 사용자에게 적립 건수가 갑자기 폭증한다 사용(차감)이 짧은 시간에 몰려 들어온다 만료(EXPIRE)가 특정 기간에 비정상적으로 많다(정책/배치 오류 가능성) ref_id가 없는 이벤트가 예상보다 많다(추적성 저하) 이런 패턴은 개별 행을 보는 SELECT만으로는 잘 안 보입니다. 그룹으로 묶어서 요약 해야 드러납니다. 이번 글은 GROUP BY로 “요약 테이블을 만들고”, HAVING으로 “요약 결과에 조건을 거는” 흐름을 익히는 데 집중합니다. 이 단원의 목적: “원장 → 요약 → 필터”로 운영 질문에 답하기 HAVING은 WHERE와 비슷해 보이지만, 역할이 다릅니다. WHERE는 행을 고르는 조건 , HAVING은 그룹(집계 결과)을 고르는 조건 입니다. 이번 글의 목표는 아래 3가지입니다. GROUP BY로 유저/기간/행위별 요약을 만든다 HAVING으로 “집계값 기준 필터링”을 정확히 적용한다 실제 포인트 운영에서 의미 있는 탐지 예시를 여러 형태로 만든다 1) WHERE vs HAVING: 역할이 다르다 WHERE는 집계 이전에 적용됩니다. 즉, 어떤 행을 집계에 포함시킬지 결정합니다. HAVING은 집계 이후에 적용됩니다. 즉, 집계 결과가 특정 조건을 만족하는 그룹만 남깁니다. 예시: 최근 30일 내역만 대상으로 “행위별 건수”를 본다 SET @uid := '11111111-1111-1111-1111-111111111111'; SELECT action_type, COUNT(*) AS cnt FROM point_history WHERE uid = @uid AND hide = 0 AND created_at >= NOW() - INTERVAL 30 DAY ...

집계 기초: SUM/COUNT로 잔액·기간별 적립/사용 리포트 만들기

포인트 시스템의 “진짜 쓸모”는 원장(point_history)에 기록된 사실을 사람이 이해할 수 있는 형태로 요약하는 데서 나옵니다. 화면에서는 잔액 한 줄로 보이지만, 그 뒤에는 적립/사용/만료/취소가 뒤섞인 수많은 기록이 존재합니다. 그래서 포인트 시스템에서 SQL의 핵심은 단순 조회(SELECT)보다 집계(aggregation) 입니다. 집계는 원장 데이터를 합치고(SUM), 개수를 세고(COUNT), 기준별로 묶어서(GROUP BY) “의미 있는 숫자”로 바꾸는 과정입니다. 이 단원의 목적: 원장 데이터로 ‘요약 결과’를 만드는 습관 만들기 이번 글에서는 아래 3가지를 목표로 합니다. SUM으로 잔액을 만들고, 기간별 합계를 안정적으로 계산한다 COUNT로 이벤트 건수를 집계해 “양”을 파악한다 집계를 할 때 자주 발생하는 실수(NULL, 숨김 데이터, 중복 기준)를 피한다 준비: 집계를 위한 최소 데이터 점검 집계는 데이터가 적으면 티가 안 나지만, 데이터가 쌓이면 작은 실수가 큰 오차로 확대됩니다. 그래서 집계 전에 “필수 조건”을 먼저 확인해 두면 좋습니다. amount가 NULL이 아닌가 숨김 데이터(hide=1)가 섞여 있다면 집계에서 제외할 것인가 부호 정책(적립 + / 차감 -)이 일관적인가 아래 예시는 hide 컬럼이 있다고 가정하고, 집계에서는 hide=0만 포함하는 패턴으로 진행합니다. SET @uid := '11111111-1111-1111-1111-111111111111'; 1) 잔액 계산의 기본: SUM(amount) 가장 기본적인 잔액 조회는 “해당 사용자의 amount를 전부 더하는 것”입니다. 이 방식의 장점은 단순함과 신뢰성입니다. 원장이 올바르면, 잔액은 항상 재현 가능합니다. SELECT uid, SUM(amount) AS balance FROM point_history WHERE uid = @uid AND hide = 0 GROUP BY uid; 여기서...

UPDATE/DELETE 기초: 포인트 원장을 망치지 않는 ‘정정(ADJUST)’과 ‘소프트 삭제’ 전략

포인트 시스템을 실제로 운영하면 “완벽하게 들어오는 데이터”만 존재하지 않습니다. 이벤트 지급이 잘못되거나, 고객 문의로 일부 포인트를 되돌려야 하거나, 테스트 데이터가 섞여 들어오거나, 배치 로직이 한 번 삐끗할 수도 있습니다. 이때 초보자가 가장 쉽게 선택하는 방법이 기존 행을 UPDATE로 고치거나 DELETE로 지워버리는 것 입니다. 하지만 포인트 원장은 “기록” 자체가 신뢰의 기반이기 때문에, 과거 기록을 조용히 바꾸거나 삭제하면 나중에 추적이 불가능해집니다. 오늘은 깔끔해 보이지만, 몇 달 뒤에는 “왜 잔액이 이렇게 되었지?”를 아무도 설명할 수 없는 상태가 될 수 있습니다. 이 단원의 목적: UPDATE/DELETE를 ‘원장 철학’에 맞게 쓰는 기준 세우기 UPDATE/DELETE는 강력하지만, 원장성 데이터를 다룰 때는 사용 기준이 필요합니다. 이번 글의 목표는 아래 3가지입니다. 포인트 시스템에서 UPDATE/DELETE를 써도 되는 경우와 피해야 하는 경우를 구분한다 기록을 고치지 않고도 결과를 바로잡는 정정(ADJUST) 패턴을 만든다 물리 삭제 대신 소프트 삭제(숨김 처리)를 적용해 추적 가능성을 유지한다 1) 원장 데이터는 “수정”보다 “추가”가 기본이다 포인트 원장은 회계의 장부와 비슷합니다. 장부의 기존 줄을 지우거나 고치는 대신, 잘못된 기록이 있었다면 반대되는 기록을 추가 해 결과를 맞춥니다. 이렇게 하면 “무슨 일이 있었는지”가 남기 때문에 나중에 확인이 가능합니다. 이 관점에서 UPDATE/DELETE는 ‘금지’가 아니라 ‘제한적으로만 사용’이 됩니다. 예를 들어 메모를 수정하거나, 테스트 데이터를 숨기는 정도는 가능하지만, amount나 action_type을 바꿔서 결과를 바꾸는 방식은 위험합니다. 2) UPDATE 기본: 특정 행을 ‘조건으로’ 정확히 집어서 수정한다 UPDATE에서 가장 중요한 건 WHERE 입니다. WHERE가 없으면 테이블 전체가 수정됩니다. 그래서 UPDATE는 항...