포트폴리오 공개용으로 Macro · Wakeline 과 함께 AWS 서울 리전에 올린다. 구성은 다음과 같다.
로컬 구성을 그대로 올리면 다음이 맞지 않았다.
rds_superuser일 뿐이고, Flyway 소유자 buildrisk는 CREATEROLE만 가진다. 그런데 afterMigrate.sql이 기동할 때마다 ALTER ROLE buildrisk_app … NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION을 실행한다. PG16+는 superuser가 아니면 이 속성들을 적는 것만으로 거절한다. 그래서 첫 기동은 되고 두 번째 기동부터 api가 뜨지 않는다(VERIFICATION 169).X-Forwarded-For를 직접 접속 주소로 덮어쓴다. Caddy 뒤에서는 모든 방문자가 한 주소로 보여 IP당 레이트리밋 · 로그인 잠금이 방문자 전체에 걸린다.afterMigrate.sql: 역할이 이미 있으면 LOGIN PASSWORD만 맞춘다. 누가 속성(superuser · createdb · createrole · replication · bypassrls)을 올려 두었을 때만 NO…로 내린다. superuser가 아니면 그 문장에서 실패해 기동이 멈춘다. 최소 권한이 깨진 채 뜨지 않게 하려는 것이다.compose.aws.yml: docker-compose.yml(로컬)은 바꾸지 않는다.
sslmode=require로 붙는다. PostGIS는 RDS 마스터가 미리 설치한다(aws-deploy rds/bootstrap.sql).NOTIFY_SECRET_KEY 비움), 배치 스케줄도 끈다.nginx.conf에 include /etc/nginx/edge.d/*.conf;를 둔다(로컬에서는 비어 있어 아무 일도 하지 않는다).
ops/nginx/realip.aws.conf를 마운트한다. 사설 대역(도커 브리지 게이트웨이)에서 온 X-Forwarded-For를 믿는다.$remote_addr 자체를 바꾸므로 api 쪽(TRUSTED_PROXY_REGEX = edge 고정 IP)은 그대로다./api/ 아래 GET · HEAD가 아닌 요청을 403으로 막는다(로그인 포함). 관리자는 SSM 포트 포워딩으로 edge(127.0.0.1:3400)에 직접 붙는다.pg_dump -Fc --no-owner --no-privileges로 뜬다. 로컬 이미지가 자동으로 넣은 확장(postgis · postgis_topology · postgis_tiger_geocoder · fuzzystrmatch)과 그 스키마(topology · tiger · tiger_data)는 뺀다.pg_restore --no-owner --no-privileges로 하고 소유자 계정으로 실행한다. flyway_schema_history가 함께 오므로 Flyway는 V21을 최신으로 본다.NO…로 만든다. 다시 기동할 때마다 같은 속성을 적을 이유가 없다. 다만 누가 속성을 올려 두었다면 조용히 넘어가지 않는다.ALTER ROLE … PASSWORD에서 다른 이유로 실패한다. 역할 생성 책임도 둘로 나뉜다.NonSuperuserOwnerMigrationIT: CREATEROLE만 가진 소유자 + 마스터가 설치한 PostGIS로 Flyway를 두 번 실행한다.
permission denied to alter role — Only roles with the SUPERUSER attribute may change the SUPERUSER attribute로 실패했다.pg_signal_backend, TLS 강제, max_connections=80)spatial_ref_sys 권한 부여가 아무것도 바꾸지 않는다는 것 하나뿐이었다(마스터가 설치한 확장이라 예상된 일)./api/v1/companies 200, 로그인 · 배치 실행 POST는 403이었다.덧붙임 (2026-10-11, VERIFICATION 171) — 공개 데모 화면에서 로그인 안내를 숨김 결정 4로 공개 주소의 로그인 POST 는 앞단에서 403 이 된다. 그런데 화면은 머리말의 [로그인] 링크와 관리 화면의 ‘관리자 로그인 후 변경할 수 있습니다 · 로그인’ 안내를 그대로 보였다 — 누르면 로그인 화면까지는 가지만 로그인은 할 수 없다.
BUILDRISK_PUBLIC_DEMO 를 compose.aws.yml 에서만 "true" 로 켠다(정확히 "true" 일 때만 데모, 로컬 docker-compose.yml 은 그대로).
화면은 이미 런타임 설정을 내려 주는 /api/config 의 demo 로 알고, 데모면 익명에게 로그인 링크를 그리지 않는다(머리말 · 역할 안내 · 알림 화면).
역할 안내 문구는 ‘데모에서는 조회만 가능합니다’로 바꾼다. 세션이 있으면(아래 관리자) 지금과 같다./login 화면은 남긴다. SSM 포트 포워딩도 같은 web 컨테이너를 거치므로 관리자는 링크 없이 /login 주소로 들어간다.NEXT_PUBLIC_*)로 하면 로컬과 AWS 의 이미지가 갈린다. 런타임 설정은 /api/config 가 이미 있어 같은 이미지로 끝난다.
앞단(Caddy)이 HTML 을 고치는 방법은 화면 구조를 앞단이 알아야 하고, 로그인 403 을 보고 숨기는 방법은 방문자가 한 번은 실패해야 해서 버렸다.덧붙임 (2026-10-11, VERIFICATION 179 · 181) — 쿠키 Secure 를 고정하지 않은 이유 · 테스트 DB 를 운영과 같게
COOKIE_SECURE 는 .env 를 따른다(make env 로 만들면 false). 고정해 보았다가 되돌렸다: AWS 에서 로그인은 공개 주소(앞단이 POST 차단)가 아니라 SSM 포트 포워딩의
http://localhost:3400 에서만 일어나는데, Secure 를 고정하면 http://localhost 의 Secure 쿠키를 받지 않는 브라우저에서 관리자 로그인이 깨질 수 있다.
공개 로그인을 열 때 "true" 로 고정하고 관리 경로를 HTTPS 로 바꾼다.