제가 해결하는 문제
포트폴리오는 보통 무엇을 만들었는지를 보여준다. 그런데 회사가 사는 것은 어떤 문제가 없어지는지다. 이 문서는 그 사이를 잇는다.
아래 사례는 전부 실제로 운영 중인 사이트에서 나왔고, 숫자는 측정값이다. 추정치나 예시가 아니다.
1매출로 가는 길이 조용히 끊긴 것을 찾아낸다
증상 (회사가 실제로 하는 말)
"광고는 돌리는데 문의가 안 들어와요." "예전엔 왔는데 요즘 뜸하네요."
실제로 일어나는 일
문의 폼이 죽어도 화면은 멀쩡해 보인다. 방문자는 "문제가 발생했습니다"를 보고 그냥 나간다. 아무도 회사에 알려주지 않는다. 광고비는 그대로 나간다.
2026-08-18, 운영 중인 webcare.forblune.com 에서 실제로 이 일이 있었다. 정적 파일만 배포하는 과정에서 서버 함수가 통째로 빠졌고, 문의 접수 API 가 약 4시간 동안 405 를 돌려주고 있었다. 화면은 정상이었다.
어떻게 찾았나
배포본별로 같은 요청을 던져 경계를 좁혔다.
2일 전 배포 POST /api/lead → 400 (함수 살아 있음, 잘못된 본문을 거부)
오늘 배포 POST /api/lead → 405 (함수 없음)
400 과 405 의 차이 하나로 "언제부터, 무엇 때문에"가 확정된다. 로그를 뒤지기 전에 재현 가능한 경계를 먼저 만든다.
조치
- 유실된 서버 함수를 복구하고 배포 경로에 포함
- 배포 후 검증을 절차로 고정:
POST /api/lead가 400 이면 정상, 405 면 배포 실패 - 검증은 커스텀 도메인이 아니라 배포본 URL 로 한다. 도메인은 CDN 캐시 때문에 이미 사라진 것도 한동안 200 을 돌려준다
회사가 얻는 것
리드 유실을 시간 단위가 아니라 분 단위로 줄인다. 그리고 같은 사고가 다시 나면 배포 즉시 걸린다.
2"알림이 없어서 아무도 모르는" 구조를 끝낸다
증상
"문의가 들어오면 알림이 오죠?"
실제로 일어나는 일
위 사고를 고치고 나서 더 큰 것을 발견했다. 알림이 처음부터 없었다. 문의는 데이터베이스에 저장만 되고, 사람이 직접 조회하기 전까지 아무도 몰랐다. 저장소 전 브랜치를 훑어도 메일 발송 코드가 한 줄도 없었다.
화면에서는 이렇게 약속하고 있었다.
"담당자가 빠른 시일 내 연락드리겠습니다."
문의가 들어와도 며칠 방치될 수 있는 구조였다. 광고를 켜는 순간 바로 손실이 된다.
조치
메일 알림을 붙이되, 알림이 문의를 잡아먹지 않도록 설계했다.
- 알림은 저장이 끝난 뒤에 보낸다. 메일이 실패해도 문의는 이미 저장돼 있다
- 응답을 붙잡지 않는다. 사용자는 메일 발송을 기다리지 않는다
- 실패는 반드시 로그에 남긴다. 조용히 죽는 알림이 제일 나쁘다
- 받은 메일에 그대로 답장하면 문의자에게 간다
이 설계는 곧바로 값을 했다. 첫 설정에서 API 키가 잘못 들어가 발송이 실패했는데, 문의는 정상 저장됐고 로그에 lead_notify_failed <문의ID> 401 이 남아 원인이 5분 만에 확정됐다.
회사가 얻는 것
장애를 늦게 아는 비용이 장애 자체보다 크다. 그 시간을 없앤다.
3"어느 게 최신인지 모르겠는" 상태를 끝낸다
증상
"그 파일 어디 있죠?" "고쳤는데 배포하니까 예전 걸로 돌아갔어요."
실제로 일어나는 일
운영 중인 사이트의 소스가 git 어디에도 없었다.
로컬에 후보가 8곳 있었다 — 저장소 본체, 빌드 산출물, v3, v5, v6, v6 2, v6-v2, 복구본. 라이브와 해시를 전부 대조했다.
후보 8곳 → 라이브와 일치: 0곳
Git 연동 없이 직접 업로드하는 방식이라 이력이 남지 않아 생긴 공백이었다. 이 상태에서는 누가 무엇을 고쳐도 다음 배포에 사라질 수 있다. 실제로 과거에 구버전이 최종본을 덮은 사고가 있었다.
조치
- 라이브 산출물을 그대로 내려받아 해시로 검증하고 저장소에 정본으로 고정
- 정본 규칙·배포 명령·배포 전후 검증 절차를 같은 폴더 README 에 기록
- 배포 파이프라인을 단방향으로 못박고 스크립트화
배포 스크립트에 넣은 안전장치:
| 장치 | 막는 사고 |
|---|---|
기본이 미리보기, --apply 필요 | 의도치 않은 즉시 반영 |
| 대상 저장소 origin 확인 | 엉뚱한 저장소 덮어쓰기 |
| 대상에 미커밋 변경 있으면 중단 | 남의 작업 날리기 |
| 빌드 실패 시 중단 | 낡은 산출물 배포 |
삭제 파일 개수 표시 + yes 입력 요구 | 대량 삭제 |
마지막 항목은 실제 사고에서 나왔다. 같은 날 두 저장소가 양방향으로 갈라진 것을 모르고 동기화를 돌릴 뻔했고, 파일 9개가 지워질 뻔했다.
회사가 얻는 것
인수인계가 가능해진다. 담당자가 바뀌어도 "정본이 어디냐"로 시작하지 않는다.
4크롤로 만든 사본은 사이트의 사본이 아니다
증상
"그대로 옮겼는데 왜 안 되죠?"
실제로 일어나는 일
사이트를 크롤해서 만든 사본에는 보이지 않는 것들이 빠진다.
- 서버 코드 (위 1번 사고의 원인)
og:image처럼 메타태그에만 있는 자산 — 링크가 없어 크롤에 안 잡힌다sitemap.xml에만 있는 미링크 페이지- CSS
url()안의 파일 robots.txtfavicon.icosite.webmanifest같은 관례 파일
같은 날 이 방식으로 511KB짜리 og:image 를 지웠다. 8개 페이지의 공유 미리보기 이미지였다. 더 나쁜 건 도메인이 200 을 돌려주고 있었다는 것 — CDN 캐시 때문이었다. 배포본 URL 로 확인해야 404 가 보였다.
조치
이전 배포본에서 파일을 복원하고, 배포 전 점검 목록과 "검증은 도메인이 아니라 배포본 URL 로" 를 문서에 고정했다.
회사가 얻는 것
이전·리뉴얼·업체 교체 과정에서 조용히 사라지는 것을 막는다. 사라진 줄도 모르는 자산이 가장 비싸다.
5남의 서비스 장애가 우리 장애가 되지 않게 한다
증상
"사진이 깨져 보여요. 어제는 됐는데요."
실제로 일어나는 일
디자인 쇼케이스 사이트의 사진이 전부 외부 무료 이미지 서비스를 직접 링크하고 있었다. 그 서비스가 장애를 일으키자 절반 이상이 회색 박스가 됐다. 우리 코드는 아무것도 바뀌지 않았는데 사이트가 망가진 것이다.
외부 핫링크 243건 (24개 페이지) → 0건
자체 호스팅 234개 파일 / 고유 사진 217장 / 전부 CC0 (상업적 사용 가능)
외부 이미지 의존 0
선별에서 걸러낸 함정
무료 이미지 API 는 그냥 받아 쓰면 안 된다. 실제로 두 가지에 걸렸다.
- 평면 스캔본 — 배경만 있는 이미지가 화면에서 단색 회색 박스로 읽힌다. "사진이 안 채워진 것처럼 보인다"는 지적의 실제 원인이었다
- 박물관 소장품 촬영본 — 사진 카테고리를 통과하지만 웹사이트 예시에는 전혀 맞지 않는다. 촬영기관 이름으로 걸러냈다
회사가 얻는 것
우리가 통제할 수 없는 것에 사이트를 걸지 않는다. 공짜 외부 자원은 공짜인 만큼 보장이 없다.
6"AI가 만든 티"를 만드는 실제 원인을 잡는다
증상
"뭔가 어색한데 뭐가 문제인지 모르겠어요." "성의 없어 보인다는 말을 들었어요."
실제로 일어나는 일
대개 콘텐츠가 아니라 기본기다. 가장 흔한 것이 한글 어절이 단어 중간에서 끊기는 문제다. 좁은 화면일수록 심해진다.
저희는 사이트를 점검하고 개선
합니다
이걸 보면 읽는 사람은 내용을 판단하기 전에 "검수가 안 됐구나" 를 먼저 느낀다. 그리고 그 인상은 문의 여부로 이어진다.
원인은 한 줄이다. 한글은 단어 단위 줄바꿈이 기본이 아니다.
word-break: keep-all; /* 어절 단위로만 끊는다 */
overflow-wrap: break-word; /* URL 처럼 끊을 수 없는 긴 토큰만 예외 */
overflow-wrap: anywhere 를 쓰면 keep-all 이 무시되어 도로 끊긴다. 이런 것은 알아야 보인다.
운영 중인 4개 사이트 전부에서 이 문제를 찾아 고쳤다. 쇼케이스만 25개 페이지 전부에 적용했다.
같이 잡은 것들
| 결함 | 왜 문제인가 |
|---|---|
| 필수 항목인데 화면에 표시가 없음 | 사용자가 이유를 모른 채 실패하고 이탈한다 |
| 모든 오류가 문구 하나로 뭉개짐 | 무엇이 잘못됐는지 아무도 모른다 |
| 시스템 다크 모드에서 의도와 다른 색 | 브랜드가 무너진다 |
| 입력칸이 빈 흰 상자 | 무엇을 넣어야 할지 모른다 |
필수 항목 문제는 특히 실제 손실로 이어지고 있었다. 서버는 필수로 검사하는데 화면에는 표시가 없어서, 그 항목을 안 채운 사용자는 "문제가 발생했습니다"만 보고 이유를 알 수 없었다. 제출 전에 잡아서 무엇을 채워야 하는지 알려주도록 고쳤다.
회사가 얻는 것
전환율. 내용이 좋아도 검수 안 된 인상이면 문의하지 않는다.
7기본적인 것이 빠져 있는지 확인한다
증상
"명함에 적은 주소가 안 열린다는데요?"
실제로 일어나는 일
서브도메인 9개는 전부 살아 있는데 루트 도메인만 죽어 있었다. DNS 에 웹 레코드가 아예 없어서 접속 자체가 안 됐다.
메일은 정상이라 아무도 눈치채지 못했다. 명함·메일 서명·검색 결과에서 회사 주소를 치면 안 열리는 상태가 계속되고 있었다.
조치
루트와 www 를 대표 사이트로 301 영구 이동시키되, 경로와 쿼리를 보존했다.
forblune.com/ → portfolio.forblune.com/
www.forblune.com/ → portfolio.forblune.com/
forblune.com/a/b?x=1 → portfolio.forblune.com/a/b?x=1
여기서 중요한 판단
메일 발송용 도메인 인증을 붙일 때, 루트가 아니라 서브도메인을 썼다.
루트에는 이미 Google Workspace 의 메일 설정(MX·SPF)이 걸려 있었다. 루트 SPF 에 새 발송 서비스를 끼워 넣으면 지금 쓰는 회사 메일 수신이 깨질 수 있다. 서브도메인으로 분리하면 그 위험이 없다.
작업 후 메일 레코드(MX·SPF·DMARC) 무결성을 확인했다.
회사가 얻는 것
고치러 갔다가 다른 걸 부수지 않는다. 이게 운영 중인 시스템을 만지는 일의 핵심이다.
정리 — 한 줄로
| 회사가 겪는 것 | 제가 하는 것 |
|---|---|
| 문의가 안 들어오는데 이유를 모름 | 끊긴 경로를 재현 가능한 경계로 찾아 복구하고, 재발을 배포 절차로 막음 |
| 장애를 뒤늦게 앎 | 실패가 조용히 죽지 않는 알림·로그 구조를 넣음 |
| 최신본이 어디 있는지 모름 | 정본을 확정해 저장소에 고정하고 배포를 단방향으로 못박음 |
| 이전·리뉴얼에서 뭔가 사라짐 | 크롤에 안 잡히는 자산까지 포함한 점검 절차 |
| 남의 서비스 장애로 우리가 멈춤 | 외부 의존을 걷어내고 통제 가능한 자원으로 대체 |
| "AI가 만든 티" 라는 말을 들음 | 신뢰를 깎는 기본기 결함을 찾아 고침 |
| 기본적인 게 빠져 있음 | 살아 있는 것을 부수지 않고 빠진 것만 채움 |
일하는 방식
추측으로 고치지 않는다. 원인을 확정하는 재현 가능한 경계를 먼저 만든다. 400 인가 405 인가, 해시가 같은가 다른가 — 이런 것으로 확정한 뒤에 손을 댄다.
고친 것은 반드시 확인한다. 코드가 컴파일되는 것과 사용자에게 동작하는 것은 다르다. 실제 화면에서 끝까지 제출해보고, 데이터베이스에 들어갔는지까지 본다.
같은 사고가 두 번 나지 않게 만든다. 고치고 끝내지 않고, 왜 그랬는지와 어떻게 확인하는지를 저장소에 남긴다. 다음 사람이 나일 필요가 없어야 한다.
내 실수도 그대로 적는다. 위 사례 중 1·4번은 제가 만든 사고다. 찾아내고, 원인을 규명하고, 재발 방지까지 하고, 기록으로 남겼다. 사고를 안 내는 사람은 없다. 사고를 숨기지 않고 시스템으로 막는 사람이 필요할 뿐이다.
사례의 모든 숫자는 2026-08-18 실측값이다. 검증 명령과 근거는 각 저장소의 커밋 메시지와 docs/ 에 남아 있다.