[7주차] SQLite 옮겼다가 502 맞고, 배포 뜯어고치고, 기술 선택 기준 다시 세운 주

코헤·약 19시간 전

cohiChat

목록 보기
10/10


짤 출처: This Is Fine | Know Your Meme

이번 주는 기능을 화려하게 붙인 주는 아니었다.

대신,
서비스가 진짜 운영 환경에서 살아남으려면
무엇을 먼저 정리해야 하는지 아주 세게 배운 주였다.

겉으로 보면 결과물이 적어 보인다.
UI 스크린샷도 많지 않다.

그런데 실제로는 꽤 많은 걸 건드렸다.

  • SQLite를 PostgreSQL(Supabase)로 마이그레이션
  • 배포 구조 개편
  • AWS Secrets Manager, IAM, Docker IMDS 이슈 해결
  • 운영 서버 보안/성능 점검
  • RabbitMQ, Kafka, SMTP 검증 방식 같은 기술 선택 기준 다시 정리

이번 주를 한 줄로 요약하면 이렇다.

"기능을 만들기 전에, 기능이 안 터질 바닥을 먼저 깔았다."

왜 이런 작업이 중요하냐면,
서비스는 기능만으로 운영되지 않기 때문이다.
DB가 흔들리면 기능이 무너지고,
배포가 불안정하면 릴리즈가 막히고,
시크릿 주입이 꼬이면 애플리케이션은 아예 뜨지 않는다.

즉, 이번 주는 "뭘 만들었냐"보다
"앞으로 뭘 만들어도 안 터지게 하려면 뭘 먼저 정리해야 하냐"를 파고든 주였다.


짤 출처: Pepe Silvia | Know Your Meme

SQLite는 편했는데, 왜 계속 쓰기에는 불안했을까

처음에는 SQLite로도 충분하다고 생각했다.
빠르게 시작하기에 정말 편한 DB니까.
설정이 단순하고, 로컬에서 바로 붙고, 초기 개발 속도도 빠르다.

그런데 프로젝트가 점점 이런 구조로 가고 있었다.

  • 사용자
  • 예약
  • 권한
  • OAuth 계정 연결

여기서 왜 SQLite가 점점 불안해졌냐면,
이제부터는 단순 저장소가 아니라 "관계형 정합성"이 중요한 서비스가 되기 때문이다.

예를 들어,
사용자와 예약은 연결돼 있고,
권한은 사용자 역할에 따라 달라지고,
OAuth 계정도 사용자 테이블과 자연스럽게 연결돼야 한다.

이런 상황에서는 다음이 중요해진다.

  • 참조 관계를 안전하게 유지할 수 있는가
  • 트랜잭션을 안정적으로 가져갈 수 있는가
  • 운영 환경에서 백업/복구/확장 전략을 세울 수 있는가

그래서 이번 주에는 SQLite에서 PostgreSQL(Supabase)로 마이그레이션했다.

왜 PostgreSQL이었냐면,
이번 서비스 구조에 더 잘 맞았기 때문이다.

  • 관계형 데이터 정합성을 챙기기 좋고
  • Spring/JPA와 궁합이 안정적이고
  • 운영 환경에서 확장성과 복구 전략을 가져가기 좋고
  • Supabase를 쓰면 관리형 PostgreSQL의 이점도 같이 가져갈 수 있다

즉, PostgreSQL이 더 멋있어 보여서가 아니라,
"이 서비스가 앞으로 감당해야 할 문제"에 더 맞아서 골랐다.

개인적으로 이런 선택이 중요하다고 느끼는 이유는,
기술은 유명한 걸 쓰는 게 아니라
문제에 맞는 걸 써야 하기 때문이다.


짤 출처: Distracted Boyfriend | Know Your Meme

실제로는 뭘 바꿨고, 왜 그걸 바꿨을까

설정만 슬쩍 바꾸는 수준은 아니었다.
생각보다 손댈 곳이 많았다. (자존감도 열심히 낮아졌다)

왜냐하면 DB를 바꾼다는 건 단순히 접속 주소만 바꾸는 작업이 아니라,
애플리케이션이 데이터와 대화하는 방식 전체를 바꾸는 일이기 때문이다.

1. 의존성 변경

build.gradle에서

  • SQLite 관련 의존성을 제거하고
  • PostgreSQL 드라이버를 추가하고
  • 테스트용 h2를 넣었다

왜 SQLite 의존성을 제거했냐면,
운영 DB가 더 이상 SQLite가 아닌데 관련 드라이버와 dialect가 남아 있으면
설정 충돌이나 잘못된 실행 경로가 남을 수 있기 때문이다.

왜 PostgreSQL 드라이버를 넣었냐면,
당연하지만 이제 애플리케이션이 실제로 대화해야 할 대상이 PostgreSQL이기 때문이다.
JPA를 쓴다고 해도, 결국 JDBC 드라이버가 맞아야 실제 연결이 된다.

여기서 PostgreSQL은 실제 실행 환경에서 사용하는 DB 드라이버이고,
H2는 테스트를 빠르고 가볍게 돌리기 위한 인메모리 DB다.

H2를 같이 넣었냐면

  • 테스트할 때마다 실제 Supabase/PostgreSQL에 붙지 않기 위해
  • 로컬이나 CI 환경에서 DB를 쉽게 띄우기 위해
  • 테스트마다 DB를 초기화하기 쉽게 만들기 위해

즉, 운영 DB는 PostgreSQL로 가되,테스트는 외부 인프라 의존 없이 빠르게 돌리기 위해 H2를 함께 넣었다

다만 여기에도 주의할 점은 있다.

  • H2는 PostgreSQL이 아니다
  • uuid, SQL 문법, dialect, native query 같은 부분은 차이가 날 수 있다
  • 그래서 통합 테스트까지 정확히 보려면 Testcontainers + PostgreSQL 조합이 더 안전하다

즉, H2는 빠른 단위/슬라이스 테스트에는 좋지만,
"운영 DB와 정말 똑같이 동작하느냐"까지 보장해주지는 않는다.
ㄴ 때문에 그냥 다른 방식을 쓰기로 결정해서 의견을 낼 생각이다

2. 설정 파일 수정

application.properties를 PostgreSQL 기준으로 바꿨다.

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://${SUPABASE_DB_HOST}:${SUPABASE_DB_PORT:5432}/${SUPABASE_DB_NAME:postgres}?sslmode=require
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect

왜 설정 파일을 이렇게 바꿨냐면,
애플리케이션이 어느 드라이버를 쓰고,
어느 URL로 붙고,
어느 dialect를 기준으로 SQL을 생성할지 명확하게 알려줘야 하기 때문이다.

특히 dialect가 중요한 이유는,
Hibernate가 DB마다 조금씩 다른 SQL 특성을 감안해서 쿼리를 생성하기 때문이다.
SQLite 기준으로 잡혀 있던 애플리케이션을 PostgreSQL에 붙이면,
표면상으로는 떠도 내부에서 미묘하게 깨질 수 있다.

3. 엔티티 타입 변경

BINARY(16)로 쓰던 부분을 uuid로 바꿨다.

왜 이걸 바꿨냐면,
SQLite에서는 어영부영 지나가던 타입 표현이
PostgreSQL에서는 더 명확하게 모델링되는 쪽이 낫기 때문이다.

특히 UUID는 "16바이트 데이터"가 아니라
의미 있는 식별자 타입으로 다루는 게 더 자연스럽다.
그래야 DB 스키마를 읽는 사람도 의도를 이해하기 쉽고,
JPA와 DB 사이 타입 매핑도 덜 불안하다.

4. 실제 데이터 이전

이게 제일 귀찮았다.

  • EC2에서 SQLite dump
  • 로컬로 내려받기
  • PostgreSQL 형식으로 변환
  • Supabase SQL Editor에서 실행

왜 dump를 바로 넣지 못했냐면,
SQLite와 PostgreSQL은 같은 SQL처럼 보여도
실제로는 타입 표현과 함수 체계가 다르기 때문이다.

예를 들어 이런 변환이 필요했다.

  • X'hex값' -> UUID 형식
  • epoch 밀리초 -> to_timestamp(ms/1000.0)

즉, 이번 마이그레이션은
DB 주소만 바꾸는 작업이 아니라,
데이터 형식을 통째로 번역하는 작업에 가까웠다.

왜 이게 중요하냐면,
마이그레이션이 실패하는 가장 흔한 이유가
"연결은 됐는데 데이터 의미가 깨지는 것"이기 때문이다.

연결 성공보다 중요한 건,
옮겨진 데이터가 이전과 같은 의미를 유지하는가다.


짤 출처: Pablo Escobar Waiting | Know Your Meme

DB만 바꾸면 끝일 줄 알았는데, 왜 502가 떴을까

(여기서부터 진정한 고통의 시작)

이제 DB도 바꿨겠다,
연결만 하면 끝일 줄 알았다.

당연히 아니었다.

바꾸고 나서 바로 502 Bad Gateway가 떼굴떼굴 굴러왔다.

그때 머릿속은 딱 이 상태였다.

  • 분명 바꾼 건 맞는데?
  • 왜 더 안 되지?
  • 이쯤 되면 내가 뭘 놓친 거지?

원인을 파보니까 크게 두 가지였다.

  1. .env 파일에 예전 SQLite 설정이 남아 있었다.
  2. Docker 컨테이너 안에서 AWS Secrets Manager 접근이 안 되고 있었다.

왜 첫 번째가 문제였냐면,
환경변수는 보통 "현재 코드"보다 "현재 실행 환경"을 더 강하게 지배하기 때문이다.
코드는 PostgreSQL로 바꿨더라도,
실행 시점에 SQLite 설정이 주입되면 애플리케이션은 여전히 엉뚱한 방향으로 간다.

왜 두 번째가 핵심이었냐면,
이번 장애는 단순 DB 연결 장애가 아니라
시크릿 주입 실패에서 시작된 문제였기 때문이다.

애플리케이션은 ${SUPABASE_DB_HOST} 같은 값을 읽어와야 하는데,
실제로는 그 값이 치환되지 않고 문자열 그대로 들어가고 있었다.

그래서 결국 이런 식으로 터졌다.

UnknownHostException: ${SUPABASE_DB_HOST}

이 순간 알게 됐다.
이건 DB 문제라기보다
시크릿 주입 문제였다.

즉,
코드만 들여다봐서는 안 보이는 문제였다.
환경, 권한, 네트워킹을 같이 봐야 했다.

왜 이런 구분이 중요하냐면,
원인을 잘못 정의하면 해결도 틀어지기 때문이다.
"DB 장애"라고 생각하면 연결 재시도만 하게 되고,
"시크릿 주입 문제"라고 정확히 정의해야 IMDS, IAM, Secrets Manager까지 보게 된다.


짤 출처: Disaster Girl | Know Your Meme

Docker IMDS 이슈는 왜 생겼을까

이번 주에 가장 설명하고 싶었던 포인트는 사실 이 부분이다.

AWS EC2는 메타데이터 서비스(IMDS)를 통해
자기 자신의 정보나 IAM role 기반 임시 자격증명을 가져올 수 있다.

왜 이게 중요하냐면, Secrets Manager 같은 AWS 자원에 접근할 때
"어떤 권한으로 접근하느냐"가 결국 여기서 연결되기 때문이다.

문제는 이게 EC2 본체에서는 되는데 Docker 컨테이너 안에서는 안 될 수 있다는 점이다.

이번에도 딱 그 케이스였다.

기본 hop limit이 1이면,
메타데이터 접근 범위가 제한돼서
컨테이너 내부에서는 필요한 정보를 못 읽을 수 있다.

왜 hop limit이 중요하냐면,
컨테이너는 네트워크 홉 관점에서 본체와 완전히 동일한 위치가 아니기 때문이다.
즉, "EC2에서는 되는데 컨테이너에서는 안 된다"는 일이 충분히 발생할 수 있다.

그래서 해결은 이렇게 갔다.

영구 해결

IMDSv2 hop limit을 2로 늘렸다.

그러면 컨테이너 내부에서도 메타데이터 접근이 가능해진다.

왜 이게 영구 해결이냐면,
매번 시크릿을 파일로 떨구는 우회 대신
컨테이너가 실행 환경에서 직접 필요한 권한을 읽을 수 있게 만들기 때문이다.

임시 해결

Secrets Manager 값을 직접 .env로 떨궈서,
일단 환경변수 치환이 정상적으로 되는지 확인했다.

왜 임시 해결도 필요했냐면,
지금 문제를 빨리 좁혀서 "정말 IMDS 때문이 맞는지" 확인해야 했기 때문이다.
운영 이슈는 가설을 빠르게 검증할 수 있어야 한다.

여기서 중요한 건
"그냥 다시 배포"가 아니었다.

정확히 어디에서 값이 끊기는지 확인해야 했다.

그래서 이렇게 봤다.

  • 컨테이너 안에서 IMDS 접근 가능한지 확인
  • 시크릿이 실제로 env로 내려오는지 확인
  • 어느 단계에서 값이 끊기는지 좁히기

이번 주에 강하게 배운 건 이거다.

운영 이슈는 감으로 푸는 게 아니라,
관측 가능한 지점을 늘리면서 풀어야 한다.

왜냐하면 운영 문제는 보통 한 군데가 아니라
환경, 권한, 네트워크, 애플리케이션 설정이 얽혀 있기 때문이다.


짤 출처: Is This a Pigeon? | Know Your Meme

배포 방식도 같이 뜯어고쳤다. 왜냐하면 서버가 너무 약했으니까

이전에는 서버에서 직접 빌드하는 흐름에 가까웠다.

근데 1vCPU/1GB 같은 저사양 서버에서는
이 방식이 생각보다 불안정했다.

실제로 겪은 문제는 이런 것들이었다.

  • BuildKit 세션 끊김
  • 리소스 부족
  • 실패는 했는데 어디서 죽었는지 파악 어려움

왜 이런 문제가 반복됐냐면,
서버가 "실행"도 해야 하고 "빌드"도 해야 하는 구조였기 때문이다.
저사양 환경에서 두 역할을 동시에 맡기면,
조금만 무거워져도 전체 배포가 흔들린다.

그래서 이번에는 배포를 더 단순하게 만들었다.

핵심은 이것이다.

서버에서 무거운 일을 최대한 덜 하게 하자.

프론트 배포 방식

  1. CI에서 pnpm build
  2. frontend/dist를 S3에 업로드
  3. 서버에서는 S3에서 dist만 sync
  4. Nginx 컨테이너 재시작

왜 이렇게 바꿨냐면,
프론트 빌드는 CI에서 한 번만 제대로 만들고,
서버는 결과물만 받아서 서빙하는 쪽이 훨씬 안정적이기 때문이다.
서버에서 Node 빌드까지 하게 두면 메모리와 CPU를 쓸데없이 소모한다.

서버 배포 방식

  1. SSH step에 AWS env 명시 전달
  2. Secrets Manager에서 .env 생성
  3. S3에서 app.jar 다운로드
  4. backend 컨테이너 기동

왜 이렇게 바꿨냐면,
서버도 "빌드 머신"이 아니라 "실행 머신"처럼 다루고 싶었기 때문이다.

즉,
애플리케이션은 CI에서 만들고,
서버는 검증된 산출물을 받아 실행만 하는 구조로 가져가고 싶었다.

이렇게 바꾸면 서버는
"직접 빌드하는 곳"보다는
"산출물을 받아서 실행하는 곳"에 가까워진다.

저사양 환경일수록 이 차이가 꽤 크다.

왜냐하면 실행과 빌드를 분리할수록,
서버 자원 사용량도 예측 가능해지고
배포 실패 원인도 더 단순해지기 때문이다.

=> 하하 하지만 8차 때 완전 다른 방식을 알게되어서 변경했다 ㅠㅁㅜ !!!!!


짤 출처: Drakeposting | Know Your Meme

그리고 로그를 넣었다. 왜냐하면 실패 원인을 빨리 찾아야 하니까

배포 구조만 바꾼 게 아니라,
실패 지점을 찾기 쉽게 안전장치도 넣었다.

  • set -euo pipefail
  • env/command 체크
  • aws sts get-caller-identity
  • Deploy success 로그

set -euo pipefail를 넣었냐면,
중간 명령 하나가 실패했는데도 스크립트가 어정쩡하게 계속 가는 걸 막고 싶었기 때문이다.
배포 스크립트는 "대충 끝까지 가는 것"보다
"문제가 생기면 즉시 멈추는 것"이 훨씬 안전하다.

왜 env/command 체크를 넣었냐면,
배포 실패의 상당수가 사실 코드가 아니라
빠진 환경변수, 잘못된 경로, 누락된 커맨드에서 나오기 때문이다.

aws sts get-caller-identity를 넣었냐면,
지금 이 세션이 정말 내가 의도한 AWS 자격증명으로 움직이고 있는지
가장 빠르게 확인할 수 있는 체크포인트이기 때문이다.

왜 성공 로그도 넣었냐면,
성공은 의외로 자동으로 기록되지 않는 경우가 많기 때문이다.
실패 지점만큼이나 "정상 완료 지점"도 명확해야 추적이 쉬워진다.

이걸 넣고 나서 느낀 건 명확했다.

배포는 성공하는 것만큼,
실패했을 때 빨리 원인을 찾을 수 있는지가 중요하다.

어디서 죽었는지 모르면
배포 한 번 실패할 때마다 멘탈도 같이 갈린다.

즉, 로그는 친절함이 아니라 생산성이다.


짤 출처: Hard to Swallow Pills | Know Your Meme

그래도 아직 끝난 건 아니다. 왜냐하면 운영은 항상 잔여 리스크가 남기 때문이다

이번 주에 꽤 많이 정리하긴 했지만,
리스크가 완전히 사라진 건 아니다.

남아 있는 건 이런 것들이다.

  • S3 경로 하드코딩
  • cohi-chat/prod 시크릿 의존성
  • 저사양 서버의 디스크/도커 이미지 문제
  • docker image prune -af 사용 시 이미지 재다운로드 비용

왜 이런 리스크를 굳이 적어두냐면,
운영에서는 "이미 해결한 문제"만큼
"아직 해결 못 한 문제를 정확히 알고 있는 상태"도 중요하기 때문이다.

그러니까 이번 주의 성과는
"완성"이라기보다
"이제 어디가 위험한지는 안다"에 가깝다.

근데 운영에서는 이 차이가 진짜 크다.

모르는 위험은 대응을 못 하지만,
알고 있는 위험은 체크리스트라도 만들 수 있다.

즉, 리스크를 적는 건 불안해 보이기 위해서가 아니라,
다음 개선 우선순위를 만들기 위해서다.

기술 선택도 다시 정리해봤다. 왜냐하면 구현보다 더 먼저 정해야 할 게 기준이니까

이번 주는 구현만 한 게 아니라,
"어떤 기술을 왜 쓰는가"도 다시 생각해본 주였다.

이게 왜 중요하냐면,
기술 선택 기준이 흐리면 나중에 구현이 아무리 열심히 돼도
방향 자체가 어긋날 수 있기 때문이다.


짤 출처: Daily Struggle / Two Buttons | Know Your Meme

SMTP로 이메일 살아있는지 검증? 왜 생각보다 안 통했을까

이것도 꽤 흥미로웠다.
처음 들었을 때는 진짜 그럴듯했다.

"회원가입할 때 SMTP로 실제 존재하는 이메일인지 확인하면 더 정확하지 않을까?"

처음엔 나도 이 아이디어가 꽤 똑똑해 보였다.
왜냐하면 정규식보다 한 단계 더 실제에 가까운 검증처럼 느껴졌기 때문이다.

근데 찾아보니까 현실은 냉정했다.

  • 예전에는 RCPT TO 응답으로 어느 정도 확인이 가능했지만
  • 요즘 Gmail은 항상 250 OK를 줄 수 있고
  • Microsoft는 이런 식의 검증 시도를 차단할 수 있다

즉,
SMTP로 살아있는 이메일을 판별하는 방식은
생각보다 신뢰도도 낮고 운영 리스크도 크다.

왜 이런 일이 생기냐면,
메일 서버 입장에서는 이런 검증 요청이 스팸 탐지나 계정 수집 시도로 보일 수 있기 때문이다.
즉, 검증 정확도를 높이려다가 오히려 차단 리스크를 키우는 셈이다.

그래서 실무에서는 결국
인증 메일을 실제로 발송하는 방식이 더 일반적이라는 결론에 도달했다.

왜 이 방식이 더 현실적이냐면,
실제 사용자가 메일함에 접근 가능하다는 것까지 검증할 수 있기 때문이다.
"존재하는 주소인가"보다 "실제로 이 사용자가 받을 수 있는가"가 더 중요한 문제다.

이번에 다시 느꼈다.
정교해 보이는 방법과,
실제로 많이 쓰이는 방법은 다를 수 있다.


짤 출처: Roll Safe | Know Your Meme

운영 서버 보안/성능도 한번 점검했다. 왜냐하면 터진 뒤에 보면 늦으니까

배포랑 DB만 본 게 아니라,
운영 중인 서버 상태도 간단히 점검했다.

왜 이걸 했냐면,
서비스가 일단 뜬다고 해서 안전한 건 아니기 때문이다.
운영에서는 "동작함"과 "안전함"이 전혀 다른 문제다.

확인한 주요 포인트는 이랬다.

보안 쪽

  • /actuator 노출
  • /error에서 내부 정보 노출 가능성
  • nginx/1.18.0 버전 노출
  • 엔드포인트 열거 가능성

왜 이게 문제냐면,
이런 정보들은 각각은 작아 보여도 공격자 입장에서는 시스템을 추정하는 단서가 되기 때문이다.
특히 actuator 노출은 애플리케이션 상태와 구조를 너무 친절하게 드러낼 수 있다.

상대적으로 괜찮았던 부분

  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • CORS 차단
  • .env, .git 노출 없음

왜 이런 항목도 같이 적었냐면,
보안 점검은 "뭐가 위험한지"뿐 아니라
"뭐는 이미 잘 막혀 있는지"도 같이 봐야 전체 그림이 잡히기 때문이다.

성능 쪽

간단한 부하 테스트에서는 실패율 0%였고,
응답 시간도 현재 규모에서는 아주 치명적으로 보이지는 않았다.

다만 이건 어디까지나
"지금 규모에서"의 이야기다.

그래서 다음 단계로는 이런 조치가 필요하다.

  • Actuator 비활성화 또는 인증 적용
  • 에러 상세 메시지 숨김
  • server_tokens off

왜 이런 조치를 빨리 해야 하냐면,
보안 문제는 대체로 기능처럼 "나중에 예쁘게 개선"하는 대상이 아니라
노출돼 있는 동안 계속 위험이 누적되는 종류의 문제이기 때문이다.

사고가 나고 나서 챙기는 것보다,
미리 확인해두는 게 훨씬 낫다.

이번 주에 얻은 감각 세 가지

이번 주는 되게 선명했다.

"개발 = 기능 구현"이라고만 생각하면
놓치는 게 진짜 많다.

굳이 정리하면 세 가지를 얻었다.

1. 문제는 코드 밖에서도 터진다

이번 502 이슈는
애플리케이션 로직보다
.env, IAM, Secrets Manager, Docker, IMDS 설정이 더 중요했다.

즉,
서버가 안 뜨는 이유는 코드 한 줄보다
값이 어디서 어떻게 주입되는지에 있을 수 있다.

왜 이 감각이 중요하냐면,
백엔드 개발이 결국 코드만 읽는 일이 아니라
코드가 실행되는 환경을 이해하는 일이기도 하기 때문이다.

2. 기술 선택은 이름값이 아니라 요구사항 번역이다

[[면접준비/Messaging/kafka|Kafka]], RabbitMQ, SMTP 검증 사례를 보면서 느낀 건,
유명한 기술과 맞는 기술은 다를 수 있다는 점이다.

이제는
"이게 멋있어 보인다"보다
"우리 문제를 어떤 형태로 정의해야 하지?"
를 먼저 보게 될 것 같다.

왜냐하면 좋은 선택은 기술 그 자체보다,
문제를 얼마나 정확히 모델링했는지에서 나오기 때문이다.

3. 운영 안정화는 잡일이 아니라 핵심 개발이다

이번 주는 사용자 화면에 바로 드러나는 기능은 적었다.

근데 DB와 배포를 정리하지 않았으면
다음 기능들도 계속 흔들렸을 거다.

그래서 이번 주를 지나면서,
운영 안정화도 기능 개발만큼 중요한 축이라는 걸 더 분명히 느꼈다.

왜냐하면 기반이 흔들리면,
그 위에 올린 기능은 결국 디버깅 거리만 늘리기 때문이다.

즉, 안정화는 기능 개발의 적이 아니라
기능 개발을 가능하게 만드는 전제다.


짤 출처: Boardroom Suggestion | Know Your Meme

다음 주에는 왜 이걸 이어서 해야 할까

다음 주에는 이번 주에 만든 기반을 바탕으로,
다시 사용자에게 보이는 진척까지 연결해야 한다.

왜냐하면 기반 정리는 결국 기능 진척으로 연결될 때 가장 큰 의미를 가지기 때문이다.
"안정화만 잘했다"로 끝나면 운영 일지에 가깝고,
그 위에서 다시 제품이 전진해야 개발 기록이 된다.

일단 우선순위는 이렇게 잡아두고 싶다.

  • 배포 스크립트의 하드코딩 경로와 시크릿 의존성 줄이기
  • /actuator 비활성화 또는 인증 적용
  • 에러 메시지 숨김, server_tokens off 반영
  • 기능 작업과 운영 작업 우선순위 다시 정리

왜 이런 순서냐면,
보안/배포 리스크를 먼저 줄여야 그다음 기능 개발 속도도 덜 흔들리기 때문이다.

마무리

이번 주는 블로그에 올릴 화면은 적은데,
나중에 돌아보면 엄청 중요했던 주로 남을 것 같다.

DB를 옮기고,
배포를 뜯어고치고,
시크릿과 권한 문제를 해결하고,
기술 선택 기준까지 다시 정리했다.

정리해보면 이번 주의 핵심은 결국 이거다.

서비스는 기능만으로 굴러가지 않는다.
DB, 배포, 권한, 로그, 보안 같은 기반이 정리돼야,
그 위에 올린 기능도 비로소 기능답게 동작한다.

그리고 이번 주에 내가 가장 집요하게 붙잡은 건,
"무엇을 했다"보다
"왜 그 선택을 했는가"였다.

아마 나는 앞으로도 개발하면서
뭔가를 적용할 때마다 계속 물어볼 것 같다.

  • 왜 지금 이 기술이어야 하지?
  • 왜 이 장애는 여기서 터졌지?
  • 왜 이 배포 구조가 더 안정적이지?
  • 왜 이 리스크를 지금 줄여야 하지?

결국 개발은 코드 작성이 아니라,
의사결정을 반복해서 더 나은 방향으로 밀어붙이는 일에 가깝다고 느꼈다.

다음 주에는 이번 주의 삽질이
실제로 사용자 기능 진척으로 이어지는 그림까지 만들어보는 게 목표다.

profile
하이하이

1개의 댓글

comment-user-thumbnail
약 6시간 전

재밌당.

  • 중간에 지구용사 썬가드짤도 있구.. 잼따
답글 달기