안녕하세요! 그로스로그 입니다!😊 🌱
끝난 상태도 시작 전도 아닌, 바뀌는 도중. 세 편 다 그 구간을 다루고 있었습니다.
성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 매 회차 운영진이 모든 글을 읽고, 그중 특히 마음에 남은 글을 함께 골라 큐레이션합니다.
9회차는 C 언어 기초 · 배포 장애 분석 · 모니터링 알림 설정입니다. 난이도도 영역도 제각각인데, 놓고 보니 같은 구간을 보고 있었어요.
01
🔢 [C 프로그래밍] 배열을 활용한 프로그래밍 기법
장태이 님
C 언어 학습 노트입니다. 두 가지를 다뤄요. 배열 정렬(버블정렬·선택정렬)과 2차원 배열을 지그재그로 채우기.
코드를 여러 버전으로 늘어놓은 게 특징입니다. 같은 정렬을 조금씩 바꿔가며 네 번 써봐요.
그리고 각 버전 아래에 자기가 발견한 원리를 짧게 적어뒀습니다. "배열의 인덱스와 요소의 값을 분리해 생각하여 구현" 같은 식으로요.
정리된 글이라기보다 공부하면서 남긴 흔적에 가깝습니다. 그런데 그 흔적에서 배울 게 있어요.
조금 더 들어가 보면
이 노트의 핵심은 첫머리의 두 줄입니다.
선택정렬(selection sort) — "두 항을 비교 후, 배열의 인덱스를 따로 저장. 안쪽 반복문이 끝나면 한 번만 교환"
"즉시 교환"과 "한 번만 교환"의 대비가 정확합니다.
🫧 즉시 바꾸는 방식
버블정렬 코드를 보면요.
for (i = 4; i > 0; --i) {
for (j = 0; j < i; ++j) {
if (aList[j] > aList[j + 1]) {
nTmp = aList[j];
aList[j] = aList[j + 1];
aList[j + 1] = nTmp;
}
}
}
옆자리와 비교해서 순서가 틀렸으면 바로 자리를 바꿉니다.
이름이 '버블'인 이유가 여기 있어요. 큰 값이 거품처럼 한 칸씩 밀려 올라갑니다. 한 바퀴 돌면 가장 큰 값이 맨 뒤에 도착하고, 다음 바퀴는 그 앞까지만 돌면 됩니다. 그래서 바깥 반복문이 i를 줄여가요.
글이 붙인 메모도 실용적입니다. "연접한 두 요소를 비교하는 코드가 있는지 확인하는 게 중요." 코드만 보고 버블정렬인지 구분하는 방법이에요. aList[j]와 aList[j+1]처럼 붙어 있는 두 칸을 비교하면 버블정렬입니다.
🎯 표시만 해두는 방식
선택정렬은 접근이 다릅니다. 글이 뽑아낸 원리가 이거예요.
바로 안 바꾸고 위치만 기억해둡니다. 안쪽 반복문을 다 돌면서 "지금까지 본 것 중 가장 작은 게 몇 번째였나"를 계속 갱신하고, 다 보고 나서 딱 한 번 교환해요.
노트에는 이 원리를 코드로 옮긴 버전이 여러 개 실려 있습니다. 그런데 바로 여기가 선택정렬에서 가장 자주 걸려 넘어지는 자리예요. 정리하면 이렇습니다.
for (i = 0; i < 4; ++i) {
nMinIndex = i;
for (j = i + 1; j < 5; ++j) {
if (aList[j] < aList[nMinIndex]) {
nMinIndex = j; // 표시만 갱신
}
} // ← 안쪽 반복문이 끝난 다음에
if (nMinIndex != i) { // 여기서 한 번만 교환
nTmp = aList[i];
aList[i] = aList[nMinIndex];
aList[nMinIndex] = nTmp;
}
}
두 군데가 중요합니다.
첫째, 비교 대상이 aList[i]가 아니라 aList[nMinIndex]입니다. aList[j] < aList[nMinIndex]여야 해요. 기준점이 "지금까지 찾은 최솟값"이지 "원래 자리의 값"이 아니거든요. 여기를 aList[i]로 두면 최솟값이 아니라 엉뚱한 값을 잡습니다.
둘째, 교환이 안쪽 반복문 밖에 있어야 합니다. 안에 두면 매번 자리를 바꾸게 되는데, 그러면 바꿨다가 다시 바꾸는 일이 생겨요. 선택정렬을 쓰는 이유 자체가 사라지기도 하고요.
그러니까 글이 뽑아낸 "인덱스와 값을 분리"라는 원리가 코드에서는 위치로 나타납니다. 표시(nMinIndex = j)는 안쪽에, 실행(교환)은 바깥에요.
⚖️ 왜 교환 횟수가 중요한가
두 정렬은 비교 횟수가 거의 같습니다. 둘 다 대략 n²/2번이에요. 그런데 교환 횟수가 다릅니다.
· 버블정렬 — 최악의 경우 비교할 때마다 교환
· 선택정렬 — 한 바퀴에 딱 한 번, 그러니까 총 n번
정수 배열이면 별 차이가 없어 보입니다. 그런데 덩치가 큰 값을 다룰 때 이야기가 달라져요. 구조체가 크면 한 번 교환하는 데 그만큼 복사가 일어나거든요.
"비교는 싸고 교환은 비싸다"는 상황이면 표시만 해뒀다가 한 번에 옮기는 쪽이 유리합니다. 글이 발견한 그 분리가 실제 이득이 되는 지점이에요.
〰️ 지그재그로 채우기
두 번째 주제가 재미있습니다. 5×5 배열을 뱀처럼 지그재그로 채우는 문제예요.
1 2 3 4 5 10 9 8 7 6 11 12 13 14 15 20 19 18 17 16 21 22 23 24 25
첫 번째 방법 — 인덱스를 뒤집기
for (i = 0; i < 5; ++i) {
if (i % 2 == 0)
for (j = 0; j < 5; ++j)
aList[i][j] = ++nCounter; // 순방향
else
for (j = 0; j < 5; ++j)
aList[i][4 - j] = ++nCounter; // 역방향
}
aList[i][4 - j]가 핵심입니다. j는 그대로 0부터 4까지 도는데, 넣는 자리만 뒤에서부터 채워요. 값은 순서대로 커지는데 놓이는 위치가 반대입니다.
두 번째 방법 — 증감 방향을 뒤집기
for (i = 0; i < 5; ++i) {
if (i % 2 == 0) nCounter = i * 5;
else nCounter = (i + 1) * 5 + 1;
for (j = 0; j < 5; ++j) {
nCounter += nOffset;
aList[i][j] = nCounter;
}
nOffset = -nOffset; // 토글
}
이번엔 채우는 자리는 항상 왼쪽부터입니다. 대신 값이 커졌다 작아졌다 해요. nOffset이 +1과 -1 사이를 오갑니다.
글의 메모가 정확합니다. "토글 스위치처럼 행마다 양수/음수로 변경."
같은 결과를 두 가지 방식으로 만든 것이 이 부분의 값입니다.
· 첫 번째 — 값의 순서는 그대로, 넣는 자리를 뒤집는다
· 두 번째 — 자리 순서는 그대로, 넣는 값을 뒤집는다
앞의 정렬 이야기와 같은 축이에요. 인덱스를 건드릴 것인가, 값을 건드릴 것인가. 같은 문제를 두 방향에서 푼 겁니다.
그리고 노트에 붙은 이 한 줄이 좋았습니다.
자기가 헷갈렸던 지점을 스스로에게 설명한 메모예요.
큐레이터 노트
여러 번 다시 써본 흔적이 남은 노트라서 뽑았습니다.
같은 정렬을 네 번 씁니다. 그리고 지그재그 채우기도 두 방법으로 풀어요. 한 번 돌아가면 넘어가는 게 아니라, 다르게도 해보는 방식입니다.
기초를 익힐 때 이게 꽤 중요합니다. 정렬 코드를 한 번 보고 이해했다고 생각하면 다음에 못 씁니다. 그런데 조금씩 바꿔가며 여러 번 쓰다 보면 어디가 본질이고 어디가 부수적인지 감이 잡혀요.
그리고 원리를 자기 말로 뽑아낸 것이 이 노트의 가장 큰 값입니다. "배열의 인덱스와 요소의 값을 분리해 생각하여 구현" — 교과서 문장이 아니라 직접 쓰다가 나온 문장이에요. 이런 한 줄은 코드를 잊어버려도 남습니다.
"연접한 두 요소를 비교하는 코드가 있는지 확인하는 게 중요" 같은 메모도 실용적이었어요. 남의 코드를 읽을 때 쓰는 판별법이거든요.
성장일지에 이런 기초 학습 노트가 올라오는 게 좋았습니다. 정리된 글만 기록이 아니라 공부하는 중간도 기록이니까요.
02
🚢 [TIL] 배포 직후 Server Action 에러 원인 분석과 해결 방안
정민아 님
배포 직후에만 나는 에러를 추적한 기록입니다.
Failed to find Server Action — GKE에 새 버전을 올리는 동안 접속하면 나타났어요.원인은 Deployment Skew(버전 비동기화)였습니다. 사용자 브라우저에는 구버전 화면이, 서버에는 신버전이 떠 있는 상태요.
그리고 대응을 세 계층으로 나눠 제시합니다. 인프라(Session Affinity) / 코드(deploymentId) / UX(자동 새로고침).
그런데 마지막이 정직해요. 아직 해결 안 됐습니다.
8회차 회고, 5회차 SEO에 이어 세 번째로 소개하는 저자입니다. 이번엔 장애 분석 노트예요.
조금 더 들어가 보면
시작이 인상적입니다. 거의 아무도 안 겪는 에러를 파고들었어요.
넘어가도 되는 상황이었습니다. 그런데 이유가 이어져요.
"로그에 계속 쌓이고 있고" — 사용자에게 안 보여도 어딘가에는 남고 있다는 겁니다. 그리고 로그에 정체불명의 에러가 쌓이면 진짜 문제가 생겼을 때 그게 안 보여요.
⏳ Deployment Skew — 두 버전이 겹치는 순간
원인 분석이 명료합니다.
"ID 불일치: 사용자가 구버전 화면에서 Server Action을 호출하면 구버전용 Action ID를 서버로 전송"
"처리 실패: 요청을 받은 Pod이 이미 신버전으로 교체되어 있다면, 해당 서버에는 구버전 Action ID가 존재하지 않으므로 요청을 거부"
조금 풀어보면 이렇습니다.
Next.js의 Server Action은 클라이언트에서 서버 함수를 부르는 기능이에요. 그런데 함수 이름을 그대로 보내면 위험하니까, 빌드할 때 해시 같은 ID를 붙입니다. 화면에는 그 ID가 박혀 있고, 서버는 ID로 함수를 찾아요.
문제는 빌드할 때마다 이 ID가 바뀐다는 것입니다. 코드가 조금이라도 달라지면 해시가 달라지니까요.
그래서 이런 순간이 생깁니다.
[사용자 브라우저] 구버전 화면 (구버전 Action ID를 들고 있음)
↓ 요청
[서버 Pod] 신버전으로 교체됨 (구버전 ID를 모름)
↓
"Failed to find Server Action"
롤링 업데이트가 부드럽게 진행되는 동안, 브라우저와 서버가 서로 다른 버전을 보고 있는 겁니다.
이게 특히 이 서비스에서 잘 터지는 이유도 글이 짚었어요.
그리고 웹뷰라는 점이 결정적입니다. 앱 안에 띄운 웹 화면은 오래 켜져 있어요. 브라우저 탭처럼 자주 닫고 새로 열지 않으니, 구버전 화면을 며칠씩 들고 있을 수도 있습니다.
🧭 세 계층의 대응
해법을 계층별로 나눈 게 이 글의 구조입니다.
전략 A — 인프라: Session Affinity
# backend-config.yaml 예시
apiVersion: cloud.google.com/v1
kind: BackendConfig
spec:
sessionAffinity:
type: "CLIENT_IP"
한 번 붙은 Pod에 계속 붙이는 방식입니다. 구버전 화면을 보고 있으면 구버전 Pod과 계속 대화하게 되죠.
글이 "코드 수정 없이", "가장 강력한 방법!!"이라고 적은 게 맞습니다. 다만 한계도 있어요. 구버전 Pod이 살아 있는 동안만 통합니다. 롤링 업데이트가 다 끝나면 구버전 Pod은 사라지니까요.
전략 B — 코드: deploymentId
// next.config.js
module.exports = {
experimental: {
deploymentId: process.env.GIT_COMMIT_SHA || 'v1',
},
}
deploymentId로 주입""버전 간 충돌을 프레임워크 단에서 인지, 잘못된 버전의 요청이 들어올 때 예기치 않은 런타임 에러(reading 'workers') 발생 방지"
버전을 명시적으로 붙이는 방식입니다. 요청에 배포 ID가 함께 실리니, 서버가 "이건 다른 배포의 요청이다"를 알아볼 수 있어요.
여기서 중요한 건 에러의 종류가 바뀐다는 점입니다. Cannot read properties of undefined (reading 'workers') 같은 정체불명의 런타임 에러가 "버전이 다르다"는 명확한 신호로 바뀌어요. 그러면 다음 단계에서 처리할 수 있게 됩니다.
전략 C — UX: 자동 새로고침
error.tsx)에서 특정 메시지 감지""
Failed to find Server Action 에러 발생 시 버전 업데이트가 있었음을 인지하고 window.location.reload()를 통해 신규 버전을 강제 로드"막지 못하면 복구하는 방식입니다. 어차피 새로고침 한 번이면 최신 화면이 오니까, 사용자 대신 눌러주는 거예요.
세 전략이 다른 층에서 서로 다른 일을 합니다.
| 전략 | 층 | 하는 일 |
|---|---|---|
| A. Session Affinity | 인프라 | 안 생기게 막는다 |
| B. deploymentId | 프레임워크 | 알아볼 수 있게 만든다 |
| C. 자동 새로고침 | UX | 생기면 복구한다 |
하나로 안 되니까 겹쳐 쌓는 접근이에요. A가 놓친 걸 B가 감지하고, B가 감지한 걸 C가 처리합니다.
🤷 그런데 아직 해결 안 됐다
이 글이 특별한 건 마지막 문단입니다.
원인은 다 밝혔고, 해법도 세 개나 정리했는데, 못 고치고 있습니다.
이유가 두 가지예요. 담당 포지션이 비어 있고, 권한이 없습니다. 8회차 회고에서 기획자 한 자리가 비어 요청이 몰렸다던 그 이야기와 같은 구조입니다. 자리가 비면 그 일이 사라지는 게 아니라 어딘가에서 멈춰 있어요.
그리고 이 판단이 신중합니다.
할 수 있다고 해서 하는 게 아니라는 것. 프로덕션 로드밸런서 설정은 잘못 건드리면 에러 하나 고치려다 서비스 전체를 내릴 수 있는 영역이니까요.
그래서 결론이 이렇습니다.
할 수 있는 것(코드)부터 하고, 못 하는 건 못 한다고 적었습니다.
큐레이터 노트
사용자가 안 보는 에러를 파고든 글이라 뽑았습니다.
"실 사용자가 이 에러를 맞는 경우는 거의 없겠지만"에서 멈춰도 아무도 뭐라 안 했을 겁니다. 그런데 로그에 쌓이고 있다는 것을 이유로 들어 끝까지 갔어요. 로그를 깨끗하게 유지하는 것 자체가 관측 능력이라는 감각입니다.
계층별로 나눈 대응이 특히 좋은 구조였습니다. 인프라 / 프레임워크 / UX. 그리고 각 층에서 역할이 다르다는 걸 명확히 했어요. 막기 / 알아보기 / 복구하기.
해결 못 했다고 쓴 것이 이 글을 더 유용하게 만듭니다. 같은 에러로 검색해 들어온 사람에게 "이 문제는 인프라 권한이 필요할 수 있다"는 정보가 함께 가거든요. 원인만 알고 못 고치는 상황도 실무의 흔한 상태입니다.
팀에 공유하고 개인 블로그에도 남긴 것도 눈에 띄었어요. "유사한 문제를 다른 서비스에서도 맞닥뜨릴 수 있다 보니 따로 팀내에 공유를 하긴 했는데, 개인적으로도 정리해두고 싶어서." 사내 문서는 사내에만 남는데, 블로그에 남기면 다음에 같은 문제를 만난 남에게도 갑니다.
03
🔔 [Grafana] Slack Alert
사범기 님
Grafana에서 에러가 나면 슬랙으로 알림을 보내는 설정을 처음부터 끝까지 따라간 글입니다.
네 단계예요. 슬랙 웹훅 만들기 → Grafana에 연결 → 알림 정책 수정 → Alert rule 설정.
그리고 각 설정 항목이 무슨 뜻인지 짚습니다. Pending period, No data 처리, 쿼리 조건까지요.
시작 문장이 실용적입니다. "스타트업이나 비용을 고려해야 한다면 Grafana로도 충분히 가능하다고 볼 수 있을 거 같습니다."
6회차 SAM 배포 글에 이어 두 번째로 소개하는 저자입니다. 이번에도 직접 세팅한 기록이에요.
조금 더 들어가 보면
먼저 왜 Grafana인지가 명확합니다.
더 좋은 게 있다는 걸 알고 있으면서 이걸 고른 이유를 밝혔어요. 3회차 김종진 님이 Particify를 고를 때와 같은 방식입니다. 조건이 다른 사람은 다른 답을 고를 수 있게 열어둔 서술이에요.
📊 대시보드에서 알림으로
Grafana를 쓰는 사람 대부분은 대시보드로 씁니다. 그래프를 그려놓고 들여다보는 거죠.
그런데 대시보드에는 한계가 있어요. 누가 보고 있어야 합니다. 새벽 3시에 에러가 터지면 아침에야 알게 됩니다.
알림은 방향을 뒤집습니다. 내가 보러 가는 게 아니라 저쪽에서 찾아오게 만드는 거예요. 이 글은 그 전환을 다룹니다.
3회차 이준호 님의 장애 리포트에서 "추측하지 말고 측정하라"며 커넥션 풀 상태를 로깅하는 코드를 만들었던 것과 같은 계열이에요. 다만 거기는 기록까지였고, 여기는 알림까지 갑니다.
🔗 웹훅으로 잇기
첫 단계가 슬랙 웹훅입니다.
"초록색 버튼인 reinstall to app을 선택하고 전달받을 채널을 선택합니다."
Incoming Webhook은 "이 주소로 POST를 보내면 이 채널에 메시지가 뜬다"는 URL을 하나 발급받는 겁니다. 슬랙 API를 배울 필요도, 토큰을 관리할 필요도 없어요. URL 하나면 됩니다.
그리고 Grafana 쪽 설정.
"Webhook URL에 Slack에서 생성한 웹훅 URL을 입력합니다.(입력 후 우측 상단에 테스트 버튼으로 메시지가 전달이 되는지 확인해 보세요.)"
테스트 버튼을 먼저 눌러보라고 짚은 게 좋았습니다.
이게 왜 중요하냐면 — 알림은 평소에 안 오는 게 정상입니다. 그래서 설정이 잘못됐어도 티가 안 나요. 조용한 게 "문제가 없다"인지 "알림이 고장 났다"인지 구분이 안 되는 겁니다.
진짜 장애가 났을 때 처음 알게 되는 상황이 최악이죠. 그래서 연결 직후 테스트가 필수입니다.
⏱️ Pending period — 얼마나 기다릴 것인가
설정 항목 설명 중에서 이게 가장 실전적입니다.
"Pending period는 조건 위반하면 즉시 알림을 발송할지 지속된 상태를 보고 발송할지 설정하는 것입니다."
"즉시 보낼 것인가, 지속되는지 보고 보낼 것인가."
이게 알림 설계의 핵심 저울입니다.
즉시 보내면 — 반응이 빠릅니다. 대신 잠깐 튀었다가 저절로 돌아온 것까지 다 알림이 옵니다. 그런 알림이 쌓이면 사람이 알림을 무시하기 시작해요. 그러면 진짜 장애도 놓칩니다.
기다렸다 보내면 — 진짜 문제만 옵니다. 대신 알아채는 게 그만큼 늦어져요.
정답이 없고 서비스마다 다릅니다. 그래서 글이 마지막에 이렇게 적은 게 맞아요.
🈳 No data를 어떻게 볼 것인가
가장 미묘한 설정이 이겁니다.
"데이터가 없다"는 무슨 뜻일까요?
에러 로그를 세는 알림이라면 — 데이터가 없다 = 에러가 없다 = 좋은 것입니다. 그래서 OK가 맞아요.
그런데 다른 종류의 알림이라면 반대입니다. 서버 상태를 주기적으로 보내는 지표라면, 데이터가 없다 = 서버가 죽었다일 수 있거든요. 그러면 Error가 맞습니다.
같은 설정이 지표에 따라 정반대 의미를 갖는 거예요. 글쓴이는 자기 경우가 어느 쪽인지 판단하고 골랐습니다. 선택과 이유를 함께 적은 것이 좋았어요.
🧪 일부러 오류를 내본 것
마지막에 이 한 줄이 있습니다.
설정하고 끝낸 게 아니라 실제로 터뜨려봤어요.
앞서 말한 그 문제 때문에 이게 꼭 필요합니다. 알림은 평소에 조용한 게 정상이라, 동작하는지 확인하려면 일부러 조건을 만들어야 해요.
그리고 조건이 명확합니다.
"C는 B의 결괏값이 0보다 크면 알림을 발생시킵니다."
"해당 설정은 에러가 1건이라도 발생하면 slack 알림으로 보낸다고 생각해 주시면 될 거 같습니다."
"에러 1건이라도"로 시작한 겁니다. 가장 단순한 조건이에요. 그리고 이렇게 이어갑니다.
"초기 설정과 테스트까지"라고 범위를 밝혔습니다. 단순하게 시작해서 필요해지면 다듬는 순서예요.
큐레이터 노트
설정 항목의 의미까지 적은 글이라 뽑았습니다.
이런 종류의 글은 클릭 순서만 나열하기 쉽습니다. 여기 누르고 저기 입력하세요 하는 식으로요. 그러면 화면이 조금만 바뀌어도 못 따라가고, 왜 그렇게 하는지는 끝까지 모릅니다.
이 글은 Pending period가 무엇을 정하는지, No data를 왜 OK로 뒀는지를 설명해요. 자기 서비스에 맞게 다르게 정할 수 있는 정보입니다.
"일부러 오류를 발생하여 테스트를 해본 결과"도 좋았습니다. 알림 설정에서 가장 놓치기 쉬운 게 검증이거든요. 안 울리는 게 정상이라 고장 나도 모릅니다.
그리고 6회차 글과 같은 문장으로 시작하는 것도 눈에 띄었어요.
같은 태도가 반복되는 것이 이 저자의 특징입니다. 그리고 참고한 글 링크도 매번 남기고요.
무엇보다 비용 이야기로 시작한 것이 실용적이었습니다. "스타트업이나 비용을 고려해야 한다면" — 도구 선택에 예산이라는 조건이 있다는 걸 인정하고 들어가요. 그게 실제 판단이 이뤄지는 방식입니다.
💡 세 편을 겹쳐 읽으며
C 배열 정렬, 쿠버네티스 배포 장애, 그리고 슬랙 알림 설정. 난이도도 층위도 다 다릅니다.
그런데 셋 다 "바뀌는 도중"을 다루고 있어요.
정렬 중인 배열은 정렬된 것도 아니고 안 된 것도 아닙니다. 그리고 두 정렬의 차이가 바로 그 도중을 어떻게 다루느냐예요. 버블정렬은 볼 때마다 바로 바꾸고, 선택정렬은 다 볼 때까지 안 바꾸고 표시만 해둡니다. 결과는 같은데 도중이 다릅니다.
배포 중인 서버도 구버전도 신버전도 아닙니다. 롤링 업데이트는 서비스를 안 끊으려고 하나씩 교체하는데, 바로 그래서 두 버전이 잠깐 공존해요. Failed to find Server Action은 정확히 그 겹치는 구간에서만 납니다. 끊고 한 번에 바꿨다면 안 났을 에러예요.
알림 설정의 Pending period도 같은 질문입니다. 에러 하나가 떴을 때 그건 장애일까요, 지나가는 것일까요? 확정되기 전의 구간을 얼마나 기다려줄지 정하는 게 이 설정이에요.
세 번 다 "지금 반영할 것인가, 좀 더 보고 할 것인가"입니다.
· 정렬: 비교할 때마다 교환할 것인가, 다 보고 한 번에 할 것인가
· 배포: 한 번에 갈아치울 것인가, 겹치는 구간을 감수하고 하나씩 바꿀 것인가
· 알림: 조건 위반 즉시 보낼 것인가, 지속되는지 보고 보낼 것인가
그리고 어느 쪽도 공짜가 아닙니다.
바로 반영하면 반응이 빠른 대신 낭비와 오탐이 생깁니다. 기다리면 정확한 대신 늦어져요. 정렬은 교환 비용을 아끼려고 기다리고, 배포는 무중단을 얻으려고 겹침을 감수하고, 알림은 오탐을 줄이려고 기다립니다. 각자 얻는 것과 내주는 것이 있어요.
마지막으로 하나 더. 세 글 다 "정상 상태에서는 안 보이는 것"을 보고 있습니다.
정렬이 끝난 배열만 보면 두 알고리즘의 차이를 알 수 없고, 배포가 끝난 서버에서는 그 에러가 안 나고, 아무 일 없는 서비스에서는 알림이 잘 되는지 알 수 없어요.
그래서 세 글 다 일부러 그 구간을 들여다봅니다. 코드를 여러 버전으로 써보고, 로그를 뒤져 재현 조건을 찾고, 일부러 오류를 발생시켜 봐요.
잘 돌아갈 때 확인해두지 않으면, 확인이 필요한 순간에는 이미 늦습니다.
이번 회차에서 가져갈 것
1. 버블정렬은 즉시 교환, 선택정렬은 표시 후 한 번만.
비교 횟수는 비슷한데 교환 횟수가 다릅니다. 덩치 큰 값을 옮길수록 이 차이가 커져요.
2. 선택정렬의 두 자리를 조심하기.
비교 기준은 aList[nMinIndex](지금까지 찾은 최솟값)여야 하고, 교환은 안쪽 반복문 밖에 있어야 합니다. 안에 두면 바꿨다 되돌리는 일이 생겨요.
3. 지그재그 채우기는 두 방향으로 풀 수 있다.
자리를 뒤집거나(aList[i][4-j]) 값의 증감을 뒤집거나(nOffset = -nOffset). 같은 결과인데 건드리는 대상이 달라요.
4. Failed to find Server Action은 버전이 겹칠 때 난다.
Server Action ID는 빌드마다 바뀝니다. 브라우저에 구버전 화면이 남아 있는데 서버가 교체되면 ID를 못 찾아요. 웹뷰처럼 오래 켜져 있는 화면에서 특히 잘 납니다.
5. 배포 스큐는 세 층에서 겹쳐 막는다.
인프라(Session Affinity)로 안 생기게, 프레임워크(deploymentId)로 알아볼 수 있게, UX(자동 새로고침)로 생기면 복구. 하나로는 안 됩니다.
6. 사용자에게 안 보여도 로그에 쌓이면 문제다.
정체불명의 에러가 쌓이면 진짜 문제가 생겼을 때 그게 안 보입니다.
7. 할 수 있다고 해서 건드리는 게 아니다.
프로덕션 인프라 설정은 에러 하나 고치려다 서비스를 내릴 수 있는 영역이에요. 권한과 담당이 정리된 뒤에 하는 게 맞습니다.
8. 알림은 연결하자마자 테스트하기.
조용한 게 정상이라 고장 나도 티가 안 납니다. 진짜 장애 때 처음 알게 되는 게 최악이에요.
9. Pending period로 오탐과 지연을 저울질하라.
즉시 보내면 잠깐 튄 것까지 오고, 그런 알림이 쌓이면 사람이 알림을 무시하기 시작합니다.
10. No data는 지표에 따라 뜻이 반대다.
에러 카운트라면 데이터 없음 = 정상(OK)이고, 서버 상태 지표라면 데이터 없음 = 서버 죽음(Error)일 수 있어요.
11. 일부러 터뜨려서 확인하기.
설정만 하고 끝내면 동작하는지 알 방법이 없습니다.
좋은 글 남겨주신 장태이 님, 정민아 님, 사범기 님께 감사드립니다! 😊
다음 큐레이션으로 다시 찾아오겠습니다! 🌱
- 에디터 · 성장일지 큐레이터 -
※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.