"웹 개발부터 배포, 운영까지 (1/8) — 로컬에서는 되는데 운영에서는 왜 깨질까"

웹 개발 입문자가 가장 먼저 마주치는 운영의 벽은 코드가 아니라 환경 차이다. 로컬에서의 성공은 통제된 실험이고, 운영은 통제되지 않은 현실이다.

핵심 요약

  • 로컬 환경은 개발자가 알고 있는 조건 위에서 움직이지만, 운영 환경은 사용자·서버·네트워크·보안 정책이 함께 얽힌다.
  • 같은 코드라도 운영에서는 환경 변수, 데이터 상태, 파일 시스템, 외부 API, 배포 방식 차이 때문에 다르게 동작할 수 있다.
  • "로컬에서 된다"는 말은 출발점일 뿐이며, 운영 준비란 코드보다 경계를 이해하는 일에 가깝다.
  • 이후 글에서 다룰 Docker, API/MCP, Supabase, 빌드, 배포, HTTPS는 모두 이 환경 차이를 줄이기 위한 장치들이다.

로컬 성공과 운영 성공은 무엇이 다른가

로컬 개발은 대개 한 사람이 통제 가능한 작은 실험실에서 이뤄진다. 사용하는 Node 버전도 알고 있고, .env 파일도 직접 만들었고, 데이터베이스도 비어 있거나 단순한 샘플 데이터만 들어 있다. 문제가 생기면 터미널을 보고 바로 고칠 수 있다.

반면 운영 환경은 다르다. 서버는 별도 머신에 있고, 실행 계정과 권한이 나뉘며, 접속하는 사용자는 여러 명이다. 요청은 네트워크를 거치고, 외부 API는 지연되거나 실패할 수 있고, 데이터는 이미 예외 상황을 품고 있다. 즉 운영은 "코드가 맞는가"만 묻지 않고 "이 코드가 실제 조건에서도 버티는가"를 묻는다.

그래서 로컬 성공은 구현 확인이고, 운영 성공은 시스템 적합성 확인이라고 보는 편이 정확하다.

가장 먼저 갈라지는 것은 실행 환경이다

운영에서 흔히 터지는 첫 번째 차이는 실행 환경이다. 로컬에서는 npm install 후 바로 돌았지만, 운영 서버는 다른 Node 버전을 쓰고 있을 수 있다. 로컬은 macOS인데 운영은 Linux일 수 있고, 로컬 파일 경로는 대소문자를 느슨하게 다루지만 운영은 엄격할 수 있다.

이 차이는 사소해 보여도 결과는 크다. 어떤 라이브러리는 OS에 따라 바이너리 설치 방식이 달라지고, 빌드 도구는 런타임 버전에 민감하며, 환경 변수 이름 하나만 달라도 애플리케이션이 부팅조차 못 할 수 있다.

이 지점에서 Docker가 등장하는 이유가 분명해진다. Docker는 편의 기능이 아니라, "어디서 실행해도 같은 조건을 재현하자"는 환경 통제 장치다. 다음 글이 바로 이 문제를 다룬다.

데이터와 상태는 로컬보다 훨씬 더 지저분하다

입문 단계에서는 기능이 동작하는지만 확인하느라 데이터 상태를 단순하게 놓는 경우가 많다. 하지만 운영 데이터는 비어 있지 않고, 중복되며, 예외 입력을 포함하고, 이미 오래된 기록도 섞여 있다.

예를 들어 로컬에서는 회원 3명으로 잘 돌던 기능이 운영에서는 수만 건 데이터 때문에 느려질 수 있다. 로컬에서는 모든 필드가 채워졌지만 운영에서는 과거 버전 사용자 때문에 일부 값이 null일 수 있다. 로컬에서는 한 사람이 동시에 한 번만 누르던 버튼이 운영에서는 여러 사용자가 연속 호출할 수도 있다.

즉 운영 문제의 상당수는 "코드가 틀렸다"보다 "데이터가 현실을 반영한다"에서 나온다. Supabase나 다른 데이터 계층을 다룰 때도 CRUD 자체보다 전처리·후처리·검증 경계를 나누는 이유가 여기에 있다.

네트워크와 외부 의존성은 로컬에서 잘 보이지 않는다

로컬에서 내부 함수 호출은 거의 즉시 끝난다. 그러나 운영에서는 브라우저, CDN, 프록시, 백엔드, 데이터베이스, 외부 API가 모두 네트워크 경계로 분리된다. 이 순간부터 지연시간, 타임아웃, 재시도, 인증 실패가 실제 문제가 된다.

특히 입문자가 자주 놓치는 점은 "API를 호출했다"와 "사용자 경험이 안정적이다"가 같은 말이 아니라는 사실이다. 외부 LLM API를 붙여도 응답이 늦을 수 있고, 인증 토큰이 만료될 수 있으며, 도구 선택을 잘못하면 비용이 커질 수 있다. MCP 같은 도구 계층도 결국 이 외부 의존성을 더 안전하게 조직하려는 시도다.

운영은 로컬보다 느리고, 더 자주 실패하며, 그 실패를 설계 안으로 넣어야 한다.

보안과 권한은 운영에서 비로소 중심이 된다

로컬 개발에서는 관리자 권한으로 대부분을 처리해 버리기 쉽다. 하지만 운영에서는 누가 무엇에 접근할 수 있는지가 핵심 조건이 된다. 데이터베이스 비밀키, OAuth 토큰, 서비스 계정, 배포 권한, 도메인 설정 권한이 모두 분리된다.

이 차이는 단지 보안 규정 때문만이 아니다. 권한 분리는 시스템 실수를 줄이는 운영 구조이기도 하다. 프론트엔드에 있으면 안 되는 키가 브라우저로 노출될 수 있고, 서버만 가져야 하는 비밀이 잘못 배포될 수도 있다. 그래서 환경 변수 분리, 서버와 클라이언트 역할 분리, 프록시와 인증 계층 분리가 중요해진다.

운영을 이해한다는 것은 권한이 코드 밖에서도 구조를 만든다는 사실을 이해하는 일이다.

운영 관점에서 좋은 개발은 무엇을 준비하는가

좋은 개발은 기능을 만드는 데서 끝나지 않는다. 운영에 올릴 때 깨질 가능성이 큰 경계들을 미리 줄인다.

  • 실행 환경을 고정한다.
  • 환경 변수와 비밀을 분리한다.
  • 데이터 입력과 출력 경계를 정리한다.
  • 외부 API 실패를 정상 시나리오로 본다.
  • 빌드와 배포 과정을 재현 가능하게 만든다.

이 다섯 가지는 거창한 DevOps 구호가 아니라, "로컬에서만 되는 앱"을 벗어나는 최소 조건이다. 시리즈 전체가 사실상 이 다섯 가지를 한 편씩 확장하는 구조라고 볼 수 있다.

이 시리즈는 무엇을 순서대로 다룰까

이번 1편은 문제의식을 세우는 글이다. 왜 로컬과 운영이 다른지 이해해야 이후 도구들이 왜 필요한지도 보인다.

2편에서는 Docker를 통해 실행 환경을 어떻게 고정하는지 본다. 3편에서는 API, MCP, vibe-coding을 통해 외부 기능을 붙일 때 어떤 통제선이 필요한지 정리한다. 4편에서는 Supabase를 예로 들어 CRUD 뒤에 숨어 있는 전처리·후처리 경계를 설명한다. 이후 편들은 OAuth/SSO, 빌드와 번들, 배포와 nginx, HTTPS와 도메인 및 모니터링으로 이어진다.

운영 학습은 기술 이름을 외우는 일이 아니라, 경계가 왜 생겼는지 순서대로 이해하는 일이다.

참고 자료

  • docs/blog_series_webdev_ops_design.md
  • docs/blog_series_learning_format_v3.md
  • sources/260519_webdev_ops_sources.md
  • Docker Docs — What is Docker?: https://docs.docker.com/get-started/docker-overview/
  • Next.js Docs — Deploying: https://nextjs.org/docs/pages/building-your-application/deploying
  • AWS Docs — Get started with Amazon EC2: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html

이 글은 웹 개발부터 배포, 운영까지 시리즈의 1/8편이다.

댓글

이 블로그의 인기 게시물

"LLM 핵심 학습 (1/6) — 기본: 토큰화·임베딩·어텐션·위치 인코딩"

"LLM 핵심 학습 (2/6) — 파인튜닝: LoRA·QLoRA·증류·Adapter"

"ML 기초 학습 (1/9) — 머신러닝과 sklearn: 학습의 좌표계"