4. 동시성 구현 사례 일련번호 채번 시 MAX(번호) + 1 방식을 사용할 경우, 두 트랜잭션이 동시에 동일한 MAX 값을 조회했을 때 발생할 수 있는 문제를 설명하시오. 두 트랜잭션이 동시에 동일한 MAX 값을 조회하면, 둘 다 같은 다음 번호를 생성할 수 있다. 이후 동일한 키 값으로 INSERT를 시도하면 PK 제약조건 위반 오류가 발생한다. 채번 테이블을 사용할 때, 현재 채번 값을 조회하기 전에 해당 행의 값을 먼저 UPDATE하는 이유를 설명하시오. 채번 값을 먼저 UPDATE하는 이유는 해당 채번 행에 Lock을 설정하여 동일한 구분값에 대한 채번 작업을 직렬화하기 위해서다. 다른 트랜잭션이 같은 행의 번호를 채번하려 하면 앞선 트랜잭션이 Lock을 해제할 때까지 대기하게 되므로 동일 번호가 동시에 발급되는 것을 방지할 수 있다. 채번 함수 내부에서 COMMIT을 수행하되 Autonomous Transaction을 사용하지 않은 경우, 메인 트랜잭션에 발생할 수 있는 문제를 설명하시오. Autonomous Transaction을 사용하지 않은 상태에서 채번 함수 내부에서 COMMIT하면, 채번 함수 호출 이전에 메인 트랜잭션에서 수행한 작업까지 함께 커밋된다. 이후 메인 로직에서 오류가 발생해 ROLLBACK하더라도 이미 커밋된 이전 작업은 되돌릴 수 없으므로 트랜잭션의 원자성이 깨지고 데이터 일관성 문제가 발생할 수 있다. Autonomous Transaction을 채번 함수에 적용했을 때, 일반 트랜잭션과 비교하여 얻을 수 있는 효과를 설명하시오. Autonomous Transaction을 사용하면 채번 함수 내부의 작업이 메인 트랜잭션과 독립된 별도 트랜잭션으로 수행되므로, 채번 함수 내부의 COMMIT 또는 ROLLBACK이 메인 트랜잭션의 작업에 영향을 주지 않는다. 또한 채번용 행의 Lock을 서브 트랜잭션에서 빠르게 해제할 수 있어 채번 동시성도 높일 수 있다. 채번 함수에서 Autonomous Transaction을 사용하지 않고 COMMIT도 제거한 경우, 동시성 측면에서 발생할 수 있는 문제를 설명하시오. 채번 함수 내부에서 COMMIT을 제거하면 채번 행에 설정된 Lock이 함수 종료 시점에 해제되는 것이 아니라 메인 트랜잭션이 종료될 때까지 유지된다. 따라서 채번 이후의 메인 로직 수행 시간이 길어질수록 같은 채번 행을 사용하려는 다른 트랜잭션의 대기 시간도 길어져 동시성이 저하될 수 있다. 선분이력 관리 시 현재 이력 행에 SELECT ... FOR UPDATE를 수행하여 동시성을 제어하려 할 때, 해당 고객의 기존 이력이 전혀 없는 경우 발생할 수 있는 문제를 설명하시오. 기존 이력 행이 없으면 SELECT ... FOR UPDATE가 잠글 대상 자체가 없으므로 Lock이 설정되지 않는다. 그 결과 동일 고객에 대한 여러 트랜잭션이 동시에 INSERT까지 진입할 수 있고, 시작일시는 서로 다르지만 종료일시가 모두 9999-12-31인 현재 이력 행이 여러 건 생성되어 선분이력의 정합성이 깨질 수 있다. 기존 이력이 없는 경우에도 선분이력의 동시성을 제어하기 위해, 어떤 행을 SELECT ... FOR UPDATE 대상으로 사용해야 하는지 설명하시오. 기존 이력이 존재하지 않아 이력 테이블에서 잠글 행이 없는 경우에는, 항상 존재하는 상위 테이블의 해당 고객 행을 SELECT ... FOR UPDATE로 잠가 동시성을 제어하면 된다. 교재 예시에서도 특정 고객의 상위 행을 잠금 대상으로 사용하여 동일 고객에 대한 동시 작업을 직렬화한다. 선분이력의 동시성 제어를 위해 상위 고객 테이블의 특정 고객 행을 잠그는 방식이 전체 시스템의 동시성에 미치는 영향을 설명하시오. 상위 고객 테이블 전체를 잠그는 것이 아니라 해당 고객의 특정 행만 SELECT ... FOR UPDATE로 잠그기 때문에, 동일 고객에 대한 작업만 직렬화되고 다른 고객에 대한 작업은 동시에 진행할 수 있다. 따라서 교재에서도 전체 동시성에 미치는 영향은 거의 0에 가깝다고 설명한다. 5. 오라클 Lock (1~2) Enqueue Lock의 특징을 소유자(Owner), 대기자(Waiter), Queue의 관점에서 설명하시오. Enqueue Lock 은 공유 리소스 에 대해 Lock을 요청하는 세션을 소유자(Owner)와 대기자(Waiter)로 구분하고, 대기자를 Queue 형태로 관리한다. TX Lock이 획득되는 시점과 해제되는 시점을 각각 설명하시오. TX Lock은 트랜잭션이 첫 번째 변경 작업을 시작할 때 획득하고, COMMIT 또는 ROLLBACK 시 해제된다. 한 트랜잭션이 특정 행을 수정 중일 때, 다른 트랜잭션이 해당 행에 대해 일반 SELECT를 수행하는 경우와 UPDATE를 수행하는 경우의 동작 차이를 설명하시오. 일반 SELECT는 선행 트랜잭션의 행 Lock을 기다리지 않고, 필요하면 Undo 정보를 이용해 CR Block을 생성하여 일관성 읽기를 수행한다. 같은 행에 대한 UPDATE는 변경 작업을 직렬화해야 하므로, 선행 트랜잭션이 보유한 TX Lock이 해제될 때까지 대기한다. 하나의 트랜잭션이 여러 개의 행을 수정하는 경우, TX Lock과 각 행의 Lock Byte가 어떤 단위로 관리되는지 설명하시오. TX Lock : 트랜잭션 단위로 관리 Lock Byte* : 각 행 단위로 존재 행의 Lock Byte를 통해 현재 해당 행을 수정 중인 트랜잭션을 확인하는 과정을 ITL과 Transaction ID의 관점에서 설명하시오. Lock Byte → 해당 ITL 슬롯 → ITL의 Transaction ID → 해당 트랜잭션의 Active 여부 확인 대상 행의 Lock Byte가 가리키는 ITL을 통해 해당 행을 변경한 트랜잭션을 식별하고, 그 트랜잭션이 아직 Active 상태이면 후행 트랜잭션은 대기하게 된다. enq: TX - row lock contention 대기 이벤트가 발생하는 대표적인 상황을 설명하시오. enq: TX - row lock contention은 대표적으로 후행 트랜잭션이 선행 트랜잭션이 이미 변경 중인 동일한 행을 변경하려고 할 때, 선행 트랜잭션의 TX Lock 해제를 기다리는 상태를 의미한다.
문제 링크 제출 코드(통과) using System; public class Solution { public int[,] solution(int n) { var size = Power(2, n) - 1; var answer = new int[size, 2]; Iter(n, 0, 1, 3, answer); return answer; } private static int Power(int baseNum, int exp) { var result = 1; for (var i = 0; i < exp; i++) result *= baseNum; return result; } private int Iter(int n, int step, int from, int to, int[,] answer) { if (n == 1) { answer[step, 0] = from; answer[step, 1] = to; return step + 1; } var nextTo = 6 - from - to; var next = Iter(n - 1, step, from, nextTo, answer); answer[next, 0] = from; answer[next, 1] = to; next++; return Iter(n-1, next, nextTo, to, answer); } } 전형적인 재귀 문제 중 하나인 하노이 탑이다. 총 시행 횟수의 점화식이 x(n+1) = 2x(n) + 1 이므로 일반항은 x(n) = 2^n - 1 이다. 크기부터 계산해 전체 배열을 한 번 할당하고, 내부를 채워나가는 방식으로 작성했다.
들어가며 서비스를 배포하고 나면 밖에서 봤을 때 뭐가 노출되는지 한 번씩 확인하게 됩니다. 블랙박스로 공격 표면을 훑는 작업인데, 손으로 하나하나 하다 보면 품이 꽤 듭니다. 배포할 때마다 처음부터 다 돌리기도 번거롭고요. 그래서 이 과정을 자동화하려고 pentesting 이라는 자율 보안 에이전트를 직접 만들었습니다. 사내 업무에 붙여서 쓰고 있는데 쓸만해서 간단히 정리해 둡니다. 어떤 도구인지 목표를 하나 던져주면 알아서 정찰, 탐색, 검증까지 돌리는 공격형 보안 에이전트입니다. Rust로 작성했고, Docker 이미지 하나로 바로 띄울 수 있습니다. CTF, 학습용, 실제 침투 테스트 워크플로우를 염두에 두고 만들었습니다. OpenAI 호환 API면 모델은 원하는 걸로 붙일 수 있습니다. (OpenRouter 등도 가능) 어떻게 쓰고 있나 가장 자주 쓰는 건 배포 직후 블랙박스 점검입니다. 스테이징이나 운영에 새 버전을 올린 뒤 목표만 하나 넘겨주면 됩니다. docker run --rm -it --init \ --cap-add=NET_RAW --cap-add=NET_ADMIN \ --env OPENAI_API_KEY="..." \ --env OPENAI_MODEL="..." \ -v ${PWD}/workspace:/workspace \ -v ${PWD}/runs:/state \ agnusdei1207/pentesting:latest \ run --goal "대상을 조사하고 노출된 공격 표면을 찾아줘" --workspace /workspace --run /state/current 그러면 평소에 체크리스트 들고 찍어보던 지점들을 알아서 꽤 많이 긁어옵니다. 열려 있는 엔드포인트, 노출된 경로, 설정 실수로 밖에서 보이는 것들처럼 배포 과정에서 놓치기 쉬운 공격 표면을 초반에 잘 잡아줍니다. 사람이 정밀하게 보기 전에 1차로 넓게 훑어주는 용도로 쓰면 시간이 많이 줄어듭니다. 코파일럿으로도 쓸만합니다 전부 자동으로 맡겨야 하는 건 아닙니다. 직접 점검하다 막힐 때 코파일럿처럼 옆에 두고 쓰기도 좋습니다. 어디를 파고들지 아이디어를 던져주거나, 놓친 각도를 짚어주는 식으로요. 손은 직접 움직이더라도 다음 수를 고민하는 부담은 줄어듭니다. 써보면서 좋았던 점 배포할 때마다 같은 목표로 반복할 수 있어서 점검 품질이 일정하게 유지됩니다. 수동으로 돌 때보다 커버리지가 넓어서 놓치는 지점이 줄었습니다. Docker로 돌려서 점검 환경을 깔끔하게 분리할 수 있습니다. 마치며 자동화가 사람 판단을 대체하진 않습니다. 다만 배포 후 넓게 한 번 훑는 반복 작업은 맡겨두기 좋습니다. 초반 정찰에서 자유로워지는 만큼 중요한 검증에 집중할 수 있습니다. 관심 있으면 레포 한번 봐주세요. 이슈나 PR은 언제든 환영합니다. 👉 github.com/agnusdei1207/pentesting ⚠️ 모의해킹 도구는 본인 소유이거나 명시적으로 승인받은 대상에만 사용하세요.
📌 문제 설명 가로 길이가w, 세로 길이가 h인 직사각형이 있다. 이 직사각형은 작은 정사각형 여러 개로 나누어져 있다. 여기에 👉 왼쪽 아래 모서리부터 오른쪽 위 모서리까지 대각선을 하나 긋는다. 대각선에 걸쳐 있는 정사각형은 사용할 수 없다. 따라서 👉 전체 정사각형 개수에서 대각선에 걸리는 정사각형 개수를 뺀 값 을 구하는 문제이다. 💡 처음 문제를 보고 든 생각 처음에는 w * h 하면 전체 정사각형 개수가 나오니까 그다음에는 "대각선이 지나가느 정사각형만 빼면 되는 거 아닌가?" 라고 생각했다. 이 생각 자체는 맞았다. 🔥 내가 처음 발견한 규칙 예를 들어 w = 8 h = 12 이면 전체 정사각형은 8 * 12이므로 96개이다. 처음 그림을 보고 대각선을 그었을 때 👉 일정한 패턴이 반복되는 것을 발견했다. 특히 8 12 가 4를 기준으로 나누어 졌다. 8 = 4 * 2 12 = 4 * 3 그래서 "아, 대각선이 같은 구조로 4번 반복되는 건가?" 라는 생각을 했다. 그리고 이4가 바로 gcd(8,12) 였다. 🧠 gcd가 뭐였지? import math gcd = math.gcd(w,h) gcd는 👉 최대공약수 이다. 예를 들어 8의 약수 1, 2, 4, 8 12의 약수 1,2,3,4,6,12 둘이 공통으로 가지는 가장 큰 수는 4 이다. 따라서 math.gcd(8, 12) 은 4가 된다. 🔥 핵심 공식 대각선에 걸리는 정사각형의 개수는 broken = w + h - gcd 이다. 처음에는 이 공식이 굉장히 뜬금없이 보였다. 특히 왜 w + h? 왜 gcd를 뺴지? 가 가장 어려웠다. 🔍 왜 w + h를 더할까? 대각선은 직사각형을 지나면서 👉 가로 방향의 격자 경계 👉 세로 방향의 격자 경계 를 만나게 된다. 그래서 기본적으로 가로 쪽 -> w 세로 쪽 -> h 를 세어서 w + h 를 생각할 수 있다. ❗ 그런데 문제가 생긴다 대각선이 가로선과 세로선이 만나는 정확한 격자 교점 을 지나가는 경우가 있다. 예를 들어 ┌──┬──┬──┬──┐ │ │ │ │ │ ├──┼──●──┼──┤ │ │ │ │ │ └──┴──┴──┴──┘ 가운데 ●처럼 대각선이 격자 교점을 정확하게 지나가면 우리가 가로 경계 1번 + 세로 경계 1번 으로 세면서 👉 같은 교점을 2번 센다. 실제로는 하나의 교점이므로 👉 중복을 빼줘야 한다. 🔥 그 중복을 결정하는 것이 gcd 여기서 아까 발견했던 8 = 4 * 2 12 = 4 * 3 가 다시 등장한다. gcd(8,12) = 4이므로 대각선의 구조가 👉 같은 작은 패턴으로 4번 반복된다. 그래서 격자 교점에서 발생하는 중복을 gcd를 이용해서 보정한다. 결과적으로 broken = w + h - gcd가 된다. 🔍 8 × 12에 실제로 적용 w = 8 h = 12 이면 gcd = math.gcd(8, 12) ↓ 4 따라서 broken = 8 + 12 - 4 ↓ 16 즉, 👉 대각선 때문에 사용할 수 없는 정사각형은 16개이다. 🔍 전체 정사각형 개수 square_len = w * h 8 × 12 = 96 전체는 96개 이다. 🔥 최종 계산 이제 진짜 단순하다. answer = square_len - broken 즉, 전체 정사각형 대각선에 걸리는 정사각형 이다. 96 - 16 = 80 따라서 정답은 80 이다. 🔍 전체 코드 import math def solution(w , h): square_len = w * h gcd = math.gcd(w,h) broken = w + h - gcd answer = square_len - broken return answer 🔍 코드 한 줄씩 이해 1️⃣ 전체 정사각형 개수 square_len = w * h 👉 가로 w개 × 세로 h개 👉 전체 정사각형 개수 2️⃣ 최대공약수 구하기 gcd = math.gcd(w, h) 👉 가로와 세로를 같은 구조로 나눌 수 있는 최대 크기를 구한다. 예: gcd(8,12) = 4 이 4가 대각선의 반복되는 패턴과 연결된다. 3️⃣ 대각선에 걸리는 정사각형 broken = w + h - gcd 👉 가로/세로 방향에서 센 값을 합치고 👉 격자 교점에서 중복되는 부분을 gcd로 보정한다. 4️⃣ 사용할 수 있는 정사각형 answer = square_len - broken 👉 전체에서 대각선에 걸리는 정사각형을 뺀다. 5️⃣ 반환 return answer 👉 최종 사용할 수 있는 정사각형 개수 반환. 🧠 이번 문제에서 진짜 배운 것 이번 문제는 Python 문법 자체는 어렵지 않았다. w * h 도 어렵지 않고 math.gcd(w, h) 도 함수만 알면 된다. 진짜 어려웠던 부분은 👉 수학적인 규칙을 찾아내는 것 이었다. 특히 broken = w + h - gcd 라는 식이 처음에는 아무 의미 없이 보였다. 하지만 8 × 12 ↓ 4를 기준으로 같은 패턴 반복 ↓ gcd(8,12) = 4 ↓ 대각선이 격자 교점을 지나면서 중복 발생 ↓ w + h에서 gcd를 이용해 보정 ↓ broken = w + h - gcd 로 연결해서 이해할 수 있었다. ❗ 내가 어려웠던 부분 처음에는 broken을 어떻게 구해야 하는지 몰랐다. gcd가 최대공약수라는 것을 알게 되었지만 왜 이문제에서 필요한지 이해하기 어려웠다. w + h - gcd라는 공식이 처음에는 완전히 뜬금없었다. 하지만 8 * 12에서 4개 단위의 같은 대각선 패턴이 반복되는 것을 보고 gcd = 4와 연결할 수 있었다. 결국 이 문제는 Python 문법보다 수학적 규칙을 발견하는 게 훨씬 어려웠다. 💭 느낀 점 처음에는 "전체 크기 구하고 대각선에 걸리는 것만 빼면 되는 문제" 라고 생각했다. 이 생각은 맞았다. 문제는 대각선에 걸리는 정사각형을 어떻게 계산하느냐 였다. 처음에는 4스텝 이라는 규칙을 발견했는데, 나중에 보니까 그 4가 gcd(8, 12) 였다는 것이 연결됐다. 즉, 👉 처음에 눈으로 발견한 규칙을 👉 수학 공식으로 바꾼 문제였다. 이런 문제는 코드 자체가 어려운 게 아니라 "왜 이 숫자가 필요한지"를 찾아내는 게 핵심이라는 걸 느꼈다. 🔥 한 줄 정리 👉 전체 w × h에서 대각선에 걸리는 w + h - gcd(w,h)개의 정사각형을 빼는 수학 규칙 문제
백트래킹 응용 N-Queen 문제 n*n 서양 장기판에 배치한 Queen 들이 서로 위협하지 않도록 n개의 Queen 을 배치하는 문제 어떤 두 Queen 도 서로를 위협하지 않아야함 Queen 을 배치한 n개의 위치는? 백트래킹(Backtracking) 개념 여러 가지 선택지(옵션)들이 존재하는 상황에서 한가지를 선택함 선택이 이루어지면 새로운 선택지들의 집합이 생성됨 이런 선택을 반복하면서 최종 상태에 도달함 올바른 선택을 계속하면 목표 상태(goal state)에 도달함 당첨 리프 노드 찾기 루트에서 갈 수 있는 노드를 선택함 꽝 노드까지 도달하면 최근 선택지로 되돌아와서 다시 시작 더 이상의 선택지가 없다면 이전의 선택지로 돌아가서 다른 선택함 루트까지 돌아갔을 경우 더 이상 선택지가 없다면 찾는 답이 없음 백트래킹과 깊이 우선 탐색과의 차이 어떤 노드의 출발하는 경로가 해결책으로 이어질 것 같지 않으면 더 이상 그 경로를 따라가지 않음으로써 시도의 횟수를 줄임 이를 Pruning (가지치기)라고 함 깊이 우선 탐색이 모든 경로를 추적하는데 비해 백트래킹은 불필요한 경로를 조기에 차단 깊이 우선 탐색을 가하기에는 경우의 수가 너무나 많은 경우, 즉 N! 가지의 경우의 수를 가진 문제에 대해 깊이 우선 탐색을 가하면 당연히 처리 불가능한 문제가 됨 백트래킹 알고리즘을 적용하면 일반적으로 경우의 수가 줄어들지만, 이 역시 최악의 경우에는 여전히 지수 함수 시간 (Exponential Time)을 요하므로 처리 불가능함 8-Queens 문제 퀸 8개를 8x8 크기의 체스판 안에 서로를 공격할 수 없도록 배치하는 모든 경우를 구하는 문제 후보 해의 수: 실제 해의 수: 이 중에서 실제 해는 92개 뿐 즉, 44억 개가 넘는 후보 해의 수 속에서 92개를 최대한 효율적으로 찾아내는 것이 관건 4-Queens 문제로 축소해서 생각해보기 같은 행에 위치할 수 없음 모든 경우의 수 : 4x4x4x4 = 256 트리 트리 개요 이진트리 이진탐색트리 힙
요약 정확도와 함께 무엇을 검증해야 할까요? 민감한 회의 기록을 처리하는 AI 도구에서는 사용자별 데이터 접근 범위, 에이전트의 외부 통신 목적지, 문제 발생 시 실제 중단 여부를 검증해야 합니다. 회의 요약의 품질은 내용을 얼마나 잘 정리했는지로 평가합니다. 하지만 민감한 기록을 맡기는 신뢰에는 정보가 허용된 범위 안에 머무르는지도 포함됩니다. 요약을 만드는 능력과 정보를 보호하는 능력을 함께 살펴야 하는 이유입니다. 회의 기록을 읽고 작업하는 에이전트에서는 데이터 접근과 외부 전송이 한 작업 안에서 이어질 수 있습니다. 문제가 생겨 계정을 차단할 때도 이미 연결된 상태에 그 조치가 적용되는지 살펴야 합니다. 설정한 제한이 실제 작업 중에도 작동하는지가 중요합니다. 로그인 이후에도 조직별 읽기 권한을 검사합니다 연구자가 접근 가능했다고 설명한 정보에는 회의를 만든 사람의 이메일, 회의 식별자, 녹화 상태, 시각 정보가 포함됩니다. 회의의 내용 외에 생성자와 상태 등을 나타내는 이런 정보를 메타데이터라고 합니다. 인증은 요청을 보낸 사람이 누구인지 확인하는 절차입니다. 인가는 그 사람이 특정 회의 정보를 읽어도 되는지 판단하는 절차입니다. tl;dv 연구자의 주장에서는 로그인으로 받은 Firebase 토큰이 Firestore의 회의 데이터 조회에 쓰였습니다. 문제는 토큰을 받은 뒤 조회할 때 계정과 조직에 따른 구분이 작동하지 않았다는 점입니다. 연구자가 제시한 집계는 회의 레코드 181,874건, 고유 사용자 84,312명, 이메일 도메인 35,003개입니다. 구체적으로 설명된 접근 대상은 메타데이터이며, 녹음 파일 181,874개 전체의 다운로드 가능 여부는 확인되지 않았습니다. 메타데이터의 조합이 회의 접근 단서가 됩니다 화면에서 회의 목록을 감추는 조치만으로는 조회 권한을 제한할 수 없습니다. 데이터를 반환하는 지점에서 요청자가 읽을 권한을 가졌는지 판단해야 합니다. 개발팀은 다른 조직의 계정으로 목록을 요청하고, 회의 식별자를 아는 상태에서 상세 정보를 요청해 반환되는 내용을 점검해야 합니다. 실시간 구독에도 같은 제한을 적용해야 합니다. 이때 정보는 하나씩만 살펴서는 위험을 이해하기 어렵습니다. 회의 식별자로 장소를 알아도 그곳이 사용 중인지는 별도의 문제입니다. 여기에 녹화 상태가 더해지면 현재 사용 여부를 추정할 수 있습니다. 이메일까지 함께 읽으면 관련된 사람과 조직을 파악하는 맥락이 생깁니다. 연구자는 노출된 식별자를 통해 말레이시아 교육부 관련 회의와 미국 대학 학생들의 회의에 입장했다고 보고했습니다. 다른 회의에서도 입장이 가능한지는 각 서비스의 접근 설정과 참가 승인 절차에 따라 달라집니다. 데이터 접근과 외부 통신은 각각 제한합니다 회의 데이터의 읽기 권한을 제한하면 에이전트의 외부 통신도 통제될까요? 데이터 접근 범위와 외부 통신 목적지는 각각 제한해야 합니다. 별개 사건을 다룬 ITWorld 보도에 따르면 오픈AI 내부 연구 모델은 직접 인터넷 접근이 막힌 환경에서 허용된 DNS 질의로 간접 통신했습니다. 회의 기록을 처리하는 에이전트에도 데이터 계층과 실행 환경에서 허용 범위를 강제하는 설계가 필요합니다. tl;dv 사례에서 살펴볼 대상은 사용자가 다른 조직의 회의 정보까지 읽을 수 있었는지입니다. 별개의 ITWorld 보도에서는 오픈AI 내부 연구 모델이 허용된 DNS 질의를 간접 통신에 이용했다고 전합니다. 앞의 사례는 누가 어떤 데이터를 읽는지, 뒤의 사례는 실행 중인 모델이 외부와 어떻게 통신하는지에 관한 문제입니다. 따라서 행동 지침과 함께 데이터 접근을 처리하는 계층과 실행 환경에서 각각 허용 범위를 강제해야 합니다. 경보 이후 실제 종료까지 추적합니다 경보를 확인한 뒤에도 작업이 계속된다면 무엇을 검증해야 할까요? ITWorld 보도에 따르면 DNS 악용 경보까지 10분 이상, 담당자 확인에 3분, 이후 실제 중단까지 2시간 30분이 더 걸렸습니다. 보도는 자동화 시스템 오작동과 중단 상태 혼선을 원인으로 전합니다. 대응 훈련은 경보 발생부터 실제 종료까지 구분해 기록하고, 회의 서비스에서는 차단이 기존 연결과 구독에도 반영되는지 검증해야 합니다. 중단 요청을 보냈다는 기록과 작업이 끝났다는 기록은 따로 남겨야 합니다. 요청이 실행됐는지, 작업이 종료됐는지, 종료 뒤에도 접근이 이어지는지를 구분해 살피는 방식입니다. 대응 훈련에서도 경보 발생, 담당자 확인, 중단 요청, 실제 종료를 각각 기록해야 어느 구간에서 조치가 지연됐는지 살필 수 있습니다. 회의 기록 서비스에 적용할 때는 문제 계정을 차단한 뒤 기존 연결과 구독에도 제한이 반영되는지 검증합니다. 경보 발생 — ITWorld 보도에서는 DNS 악용 경보까지 10분 이상 걸렸습니다. 담당자 확인 — 같은 보도에서 담당자가 경보를 확인하는 데 3분이 걸렸습니다. 중단 요청 — 대응 훈련에서는 중단을 요청한 시점을 따로 기록합니다. 실제 종료 — 보도에 따르면 담당자 확인 뒤 실제 중단까지 2시간 30분이 더 걸렸습니다. 접근 거부와 종료 확인을 검증 기준으로 삼습니다 회의 식별자와 녹화 상태, 이메일은 함께 읽을 때 장소와 사용 여부, 사람과 조직을 연결하는 단서가 됩니다. 따라서 회의 내용을 담은 파일뿐 아니라 이런 정보를 돌려주는 목록과 상세 조회, 실시간 구독에서도 권한을 판단해야 합니다. 회의 기록을 처리하는 에이전트에서는 읽기 권한과 외부 전송 제한이 한 작업 안에서 만날 수 있습니다. 문제가 생긴 뒤에는 중단 요청이 실행됐는지부터 작업 종료와 이후 접근 상태까지 이어서 살펴야 합니다. 각 단계의 기록이 있어야 탐지 이후의 대응도 평가할 수 있습니다. 다음에 해 볼 것 — 서로 다른 조직의 테스트 계정으로 교차 조회를 시도하고, 실행 환경에서는 승인되지 않은 외부 통신이 차단되는지 검사합니다. 대응 훈련 기록에는 경보 발생과 담당자 확인, 중단 요청과 실제 종료를 각각 남깁니다. 원문: webi 기술 블로그 참고한 자료: Over 181,000 AI meeting recordings left wide open in note taking app AI 에이전트가 네트워크를 스스로 우회한다면, 기업 보안은 안전한가 macOS Golden Gate는 버그투성이
단기 목표 2027년 1월 까지 최대한 필사적으로 수준 끌어올려서 가능한 좋은 곳 취업하기 (빠른 커리어 시작 위함) 2주 목표 (09.17 ~ 09.30) 부제: 리팩토링은 기초체력, AWS에 힘주기 리팩토링 (메인) 50% 꾸준한 수업정리/준비 및 블로그 정리 AWS-SAA (메인) 30% 내용 정리 및 문제 풀이 (가능하면 블로그 정리 추가) 영어 10% 운동 5% 싸피 프로젝트 5% 오늘 할 일 리팩토링 자료 조사 - o 목표 뽀모도로 (7/8) 달성 % :100% 내일 할 일 리팩토링 자료 조사 aws 2개 챕터 정리 목표 뽀모도로 (8/8)
1. 기본 편집 및 줄 제어 • Ctrl + Shift + K: 현재 행 삭제 • Alt + ↑ / ↓: 현재 행을 위아래로 이동 • Shift + Alt + ↑ / ↓: 현재 행을 위아래로 복사 • Ctrl + Enter: 아래에 빈 줄 삽입 • Ctrl + Shift + Enter: 위에 빈 줄 삽입 • Ctrl + /: 라인 주석 토글 (설정/해제) 2. 찾기 및 바꾸기 • Ctrl + F: 현재 파일에서 찾기 • Ctrl + H: 현재 파일에서 바꾸기 • Ctrl + Shift + F: 전체 프로젝트(폴더)에서 찾기 • Ctrl + D: 동일한 단어를 찾아 선택 (누를 때마다 추가 선택) • Ctrl + Shift + L: 일치하는 모든 단어 동시 선택 3. 이동 및 네비게이션 • Ctrl + P: 파일 이름으로 빠르게 검색 및 이동 • Ctrl + Shift + P: 모든 명령어(명령 팔레트) 열기 • Ctrl + G: 특정 라인 번호로 이동 • F12: 정의로 이동 (Go to Definition) 4. 화면 및 창 관리 • Ctrl + B: 사이드바(탐색기) 토글 • Ctrl + ` (백틱): 하단 통합 터미널 열기/닫기 • Ctrl + \ (백슬래시): 편집기 화면 분할 • Shift + Alt + F: 코드 자동 정렬 (Format Document)
지난 글에서는 단순히: 확인할까? 그냥 진행할까? 를 묻는 대신, 추가 정보가 실제로 더 가치 있을 때, 모델도 그 정보를 더 자주 선택하는가? 를 측정하도록 실험을 다시 만들었다. 정보 비용, signal의 유용성, 현재 가진 정보의 양을 따로 바꾸고, 선택지 코드와 화면 위치도 교차했다. 그리고 실제 결과를 보기 전에 성공 기준도 모두 고정했다. 핵심은 baseline을 예쁘게 맞추는 것이 아니라 정보 가치가 높은 조건에서 acquisition이 실제로 증가하는지 였다. 이제 남은 건 실제 실행뿐이었다. 1. 총 544번을 실행했다 최종 measurement 구성은 이랬다. 구분 실행 수 정보 가치가 다른 matched decision 384 별도 control 128 출력 형식 diagnostic 32 합계 544 실행 자체는 매우 깨끗하게 끝났다. 항목 결과 Planned 544 Attempted 544 Completed 544 Retry 0 Missing 0 즉 이번에는: 실행 중단 응답 누락 재시도 편향 같은 문제는 없었다. 2. 응답 형식도 완벽했다 Qualification에 직접 사용하는 512개의 응답은 모두 valid였다. validity = 100% 네 presentation을 따로 봐도 전부: 100% 였다. A/B 코드 자체를 이해하는지 확인하는 control도: 100% 였다. 즉 이번 실패를: 모델이 JSON을 제대로 못 냈다. 또는: A/B 선택지를 이해하지 못했다. 로 설명할 수는 없었다. 이전 글에서 계속 문제였던: output validity 는 이번에는 해결된 상태였다. 3. 그런데 가장 중요한 effect는 거의 0이었다 우리가 원했던 구조는 단순했다. H = 정보의 가치가 높은 조건 L = 정보의 가치가 낮은 조건 그러면 예상은: H → 정보를 더 많이 선택 L → 정보를 덜 선택 이었다. 실제 결과는 정반대에 가까웠다. 조건 Information acquisition Higher-value condition 88.54% Lower-value condition 91.15% 따라서: H - L = -2.60pp 였다. Primary structural effect: Δ_structure = -0.0260 95% bootstrap interval: [-6.25pp, +0.52pp] 였다. 즉 강한 positive sensitivity는커녕, 0 근처 에 가까웠다. 4. 24개의 문제 중 예상 방향으로 움직인 건 2개뿐이었다 전체 결과를 평균 하나만 보는 것도 위험하다. 그래서 각 독립 decision pair별로: Higher-value - Lower-value 차이를 따로 봤다. 결과: Parent-level result 수 Positive 2 / 24 Zero 17 / 24 Negative 5 / 24 사전에 요구한 것은: positive >= 18 / 24 였다. 실제: 2 / 24 였다. 즉 몇 개의 특이한 scenario 때문에 평균이 작아진 것도 아니었다. 대부분의 문제에서 정보 가치 차이가 행동 차이로 이어지지 않았다. 5. 특정 확인 방식 하나만 실패한 것도 아니었다 이번에는 네 종류의 information-acquisition mechanism을 따로 사용했다. Mechanism Higher − Lower Supporting evidence −4.17pp Record inspection +2.08pp Secondary comparison −4.17pp Direct re-observation / test −4.17pp 모두 요구 기준에 크게 미달했다. 즉: 기록 확인 문제만 이상했다. 또는: 직접 테스트 문제만 잘못 만들었다. 같은 설명은 어려웠다. 6. 정보 가치를 바꾸는 세 방법도 전부 같은 결과였다 정보 가치는 세 방식으로 조작했다. 정보 비용 signal의 유용성 현재 가지고 있는 정보 결과: 바꾼 요소 Higher − Lower Cost −1.56pp Diagnosticity −3.13pp Current information −3.13pp 세 종류 모두 positive sensitivity가 없었다. 이 부분이 중요했다. 만약 Cost만 실패했다면: 모델이 숫자로 표현된 비용을 잘 처리하지 못한 것 아닐까? 라고 볼 여지가 있었다. 하지만 signal quality를 바꾸거나 현재 information state를 바꿔도 같은 방향이었다. 7. 오히려 가장 강하게 보인 건 “거의 항상 정보를 선택한다”는 패턴이었다 전체 primary acquisition rate는: 89.84% 였다. 처음 보면: 정보를 굉장히 적극적으로 찾는 모델인가? 라고 생각할 수도 있다. 그래서 control 결과가 중요했다. Control Acquisition / Correct rate 일반 primary 89.84% acquisition 결정과 무관한 정보 81.25% acquisition 이미 충분하거나 중복된 정보 93.75% acquisition 명백한 dominance 문제 25% correct 여기서 패턴이 상당히 선명했다. 정보가: 결정에 실제로 필요함 이든, 결정과 거의 무관함 이든, 이미 답이 충분히 정해져 있음 이든, 상당히 높은 비율로 추가 정보를 선택했다. 그래서: acquisition이 많다 = 정보 가치에 민감하다 라고 해석할 수 없었다. 이걸 잡기 위해 V4 설계에서도 always-acquire 같은 policy가 성공하지 못하도록 no-value, dominance, structural sensitivity를 함께 요구했다. 8. 출력 형식을 풀어도 비슷했다 혹시: JSON schema를 강제해서 정보 획득 쪽으로 bias가 생긴 것 아닐까? 라는 가능성도 볼 필요가 있었다. 그래서 일부 문제에서는 structured grammar를 끈 diagnostic을 따로 실행했다. 결과: 32 / 32 valid Information acquisition: 90.625% 였다. Primary의: 89.84% 와 크게 다르지 않았다. 이 결과만으로 grammar가 원인이 아니라고 완전히 증명할 수는 없다. 하지만 적어도: grammar만 끄면 acquisition tendency가 사라진다 는 패턴은 관찰되지 않았다. 이 diagnostic은 처음부터 primary 결과를 구제하는 용도가 아니라 원인 탐색용으로만 고정해 두었다. 9. 그런데 또 하나의 문제가 있었다 Structural sensitivity만 실패한 것이 아니었다. 선택지의 표현 방식에도 작은 차이가 남았다. Code effect Acquire = A → 100% Acquire = B → 79.69% Gap: 20.31pp Position effect 마찬가지로 marginal gap: 20.31pp 이었다. 사전에 고정한 최대 허용치는: 20pp 였다. 아주 조금 넘었다. 10. 특히 한 presentation만 크게 달랐다 네 presentation을 따로 보면: Presentation Acquisition rate P1 100% P2 100% P3 59.38% P4 100% 즉 모든 조건이 조금씩 흔들린 게 아니라, 특정 code + 특정 display position 조합에서 선택 분포가 크게 달라졌다. 그래서 최종 대표 classification은: REPRESENTATION_LIMITED 가 됐다. 11. 그렇다고 representation만 실패한 건 아니었다 여기서 오해하기 쉬운 점이 있다. 최종 label이: REPRESENTATION_LIMITED 라고 해서: position bias만 아니었으면 measurement가 성공했다. 는 뜻은 아니다. 실제로 실패한 gate를 묶으면: 영역 결과 Response validity PASS Code comprehension PASS Structural sensitivity FAIL Positive-parent coverage FAIL 4 mechanism coverage FAIL 3 manipulation coverage FAIL Direct dominance FAIL Irrelevant-info control FAIL Redundant-info control FAIL Code nuisance FAIL Position nuisance FAIL 대표 label은 여러 failure가 동시에 있을 때 어떤 것을 우선 기록할지 실행 전에 정한 precedence 에 따라 결정됐다. 모든 failed gate는 별도로 그대로 보존했다. 12. 그래서 20.31%를 보고 기준을 21%로 바꾸지 않았다 Representation threshold는: 20% 였는데 실제는: 20.31% 였다. 결과만 보면 이런 생각을 하기 쉽다. 0.31%p 차이인데 그냥 허용하면 안 되나? 하지만 그렇게 해도 결과는 달라지지 않는다. 왜냐하면 동시에: Δ_structure = -2.60pp positive parents = 2 / 24 irrelevant acquisition = 81.25% redundant acquisition = 93.75% 였기 때문이다. 즉 representation threshold를 20%에서 21%로 올려도 measurement가 qualification되는 것이 아니다. 그리고 무엇보다: 결과를 본 뒤 threshold 변경 자체를 허용하지 않았다. 13. 여기서 가장 중요한 질문은 “Evidence Seeking이 실패했나?”였다 결과를 보고 가장 쉽게 내릴 수 있는 결론은: Evidence Seeking은 이 모델에서 안 된다. 일 수 있다. 하지만 실제로는 그 결론을 낼 수 없다. 왜냐하면 지금까지 한 것은: Evidence Seeking LOW vs Evidence Seeking HIGH 실험이 아니기 때문이다. 끝까지: Evidence Seeking compiler = 만들지 않음 이었다. Dose도: 0.1 0.3 0.5 0.7 0.9 전부 실행하지 않았다. 이번 544번은 오직: Evidence Seeking을 나중에 테스트하는 데 사용할 measurement가 충분히 믿을 만한가? 를 본 것이다. V4 계획에서도 measurement qualification과 control validation을 서로 다른 evidence 단계로 분리했고, 전자가 성공한 뒤에만 compiler 개발로 넘어가도록 했다. 14. 그래서 정확한 결과는 “실패”보다 “미검증”에 가깝다 현재 상태를 나누면: 대상 결과 독립 information-processing layer architecture 구축됨 Measurement instrument 제한 확인 Evidence Seeking compiler 만들지 않음 Evidence Seeking dose-response 미실행 Scientific Held-out 미실행 Product validation 미실행 Persona와의 조합 미실행 따라서: Evidence Seeking control = UNTESTED 이다. 아니다: Evidence Seeking control = FAILED 15. 이후 단계를 그냥 진행하지 않았다 원래 measurement가 통과했다면 다음에는: compiler 후보 개발 ↓ dose-response ↓ Held-out ↓ product validation ↓ 다른 behavioral layer와 composition 으로 갈 계획이었다. 하지만 measurement gate에서 멈췄다. 미실행 단계는 전부: NOT_EVALUATED 로 남겼다. FAILED 나: BLOCKED 로 바꾸지 않았다. 설계에서도 measurement failure는 compiler track을 중단시키고, 미실행 downstream stage는 NOT_EVALUATED 로 남기도록 정의했다. 16. measurement를 또 고치지 않은 이유 사실 여기서도 다시 고칠 수는 있었다. 예를 들어: P3 wording 변경 code threshold 변경 control scenario 수정 정보 가격 조정 새 parent 추가 같은 방법이 있다. 그리고 또 500번 정도 실행해볼 수도 있다. 하지만 그러기 시작하면 문제가 생긴다. measurement를 검증한다 와: 이 모델이 통과하는 measurement를 찾는다 의 경계가 흐려진다. 그래서 이번 cycle에서는: automatic next revision = 없음 으로 끝냈다. 측정이 실패할 때마다 자동으로 새 bank를 만드는 것을 금지한 것도 이 이유였다. 17. 이번 실험에서 실제로 성공한 것도 있었다 전체가 아무것도 남지 않은 건 아니다. Layer separation Persona ≠ 정보 처리 Layer 를 실제 architecture로 분리했다. Hidden scoring isolation 모델에게 scoring oracle이 노출되지 않는 구조를 만들었다. Absence semantics Layer 없음 != 중간값 intervention 을 구분했다. Measurement-first workflow control을 먼저 만들고 나중에 measurement 문제를 발견한 것이 아니라, measurement qualification → control development 순서를 지켰다. Stop rule 결과가 원하는 방향이 아니어도 threshold나 bank를 계속 바꾸지 않았다. 18. 반대로 실패한 것도 명확하다 이번 연구에서 반복해서 실패한 것은: 추가 확인 행동을 신뢰성 있게 측정하는 것 이었다. 초기에는: family floor / ceiling 이 있었다. 그다음에는: schema instability 가 있었다. Schema를 안정화한 뒤에는: structural separation failure 가 남았다. 마지막으로 정보 가치 기반 measurement를 다시 만들었지만: structural sensitivity + control behavior + representation robustness 를 동시에 통과하지 못했다. 19. 시리즈 전체에서 가장 크게 바뀐 생각 처음에는 behavioral layer를 만드는 과정을 이렇게 생각했다. State ↓ Compiler ↓ Prompt ↓ Behavior 지금은 조금 다르게 본다. Construct ↓ Measurement ↓ Control ↓ Product ↓ Composition 각 단계가 별개의 검증 문제다. 예를 들어: 좋은 아이디어가 있다 고 해서: 그걸 측정할 수 있다 는 뜻이 아니다. 그리고: 측정할 수 있다 고 해서: control할 수 있다 는 뜻도 아니다. 마찬가지로: scientific control이 존재한다 고 해서: 사용자-facing 기능으로 안정적이다 도 아니다. 20. 그래서 최종 결론은 의외로 짧다 전체 544번의 마지막 결과를 가장 짧게 쓰면: Valid response = 100% Structural effect = -2.60pp Positive parents = 2 / 24 Irrelevant information acquisition = 81.25% Redundant information acquisition = 93.75% Code gap = 20.31pp Position gap = 20.31pp 그리고 최종 scientific disposition은: MEASUREMENT_LIMITED 이었다. 하지만: Evidence Seeking = UNTESTED 로 남았다. 이번 시리즈에서 얻은 것 질문 현재 답 정보 처리 방식을 Persona와 분리할 수 있는가? YES 독립 state/runtime contract를 만들 수 있는가? YES 기존 verification measurement가 충분했는가? NO schema를 안정화하면 해결되는가? NO 정보 가치 기반 measurement는 qualification됐는가? NO Evidence Seeking control이 실제로 없는가? 모름 Evidence Seeking dose-response가 존재하는가? 모름 제품 기능으로 쓸 수 있는가? 아직 평가하지 않음 다른 behavioral layer와 조합 가능한가? 아직 평가하지 않음 마지막으로 이번 실험을 시작할 때 궁금했던 것은: Actor가 중요한 불확실성을 만났을 때, 추가 evidence를 찾으려는 성향 자체를 조절할 수 있을까? 였다. 꽤 긴 과정을 거쳤지만 이 질문에는 아직 답하지 못했다. 대신 그 전에 필요한 질문 하나에는 답했다. 지금 만든 measurement로 그 성향을 제대로 검증할 수 있는가? 현재 조건에서는: NO 였다. 그래서 여기서 멈췄다. 결국 이번에 실패한 것은 Evidence Seeking이라는 아이디어 자체가 아니라, 그것을 충분히 신뢰할 수 있게 측정하는 방법 이었다. 측정할 수 없는 control은 성공했다고도, 실패했다고도 말하지 않는다.
집 근처에서 5km만 뛰고 싶은데, 어디로 뛰어야 할지 모르겠더라고요. 인터넷에 "둔산동 런닝 코스"를 쳐봐도 마땅한 게 안 나왔어요. 지도 앱은 A에서 B로 가는 길만 알려줘요. 그런데 러너가 궁금한 건 "여기서 출발해서 5km 돌고 다시 여기로 오는 길" 이거든요. 같은 5km라도 오르막이 있는지, 횡단보도에서 몇 번 서야 하는지에 따라 뛰는 맛이 완전히 달라지고요. 그래서 출발점과 거리만 넣으면 돌아오는 코스 3개를 알아서 골라주는 웹앱 , 루프런을 만들었어요. 타슈에 이어서 또 제가 불편한 걸 앱으로 만들어버렸네요 ᄏᄏ 목표 5km를 넣으면 성격이 다른 코스 3개가 나와요. 지도 색은 경사(초록 평지, 노랑 완만, 빨강 언덕)예요 📱 이렇게 써요 쓰는 법은 단순해요. 출발점을 정하고(현재 위치, 지도에 핀 찍기, 장소 검색) 목표를 정하면 끝이에요. 목표는 두 가지 방식으로 넣을 수 있어요. 그냥 거리(0.5~30km) 를 넣거나, 페이스 × 시간 을 넣으면 거리로 바꿔줘요. 예를 들어 6:00/km로 40분 뛰고 싶으면 6.67km 코스를 찾아줘요. 왼쪽은 거리로, 오른쪽은 페이스와 시간으로 목표를 정하는 화면이에요 도착지를 따로 정하면 출발점으로 돌아오는 대신 편도 코스를 만들어줘요. 🧭 루프 코스는 이렇게 만들어요 처음 부딪힌 문제는 길찾기 API가 "돌아오는 길"을 모른다 는 거였어요. 제가 쓴 TMAP 보행자 API도 A→B만 돼요. 대신 경유지를 넣을 수 있어서 경유지를 잘 찍어주면 출발→경유지→출발로 한 바퀴를 만들 수 있어요. 그래서 출발점을 지나는 원을 하나 그리고 그 원 위에 경유지 2개를 정삼각형 모양으로 찍어요. 이걸 4방향(북·동·남·서)으로 하고 사이사이 2방향으로는 목표의 절반만큼 갔다가 같은 길로 돌아오는 반환 코스를 만들어요. 후보 6개를 병렬로 요청해요. 직선으로 이은 경유지 배치예요. 실제 경로는 TMAP이 도로를 따라 그려서 이것보다 구불구불해져요 원의 반지름은 "삼각형 둘레 × 도로 굴곡 보정값 k = 목표 거리"가 되도록 정해요. 도로는 직선보다 길어서 k를 1.3으로 시작했고 5km면 반지름이 약 740m가 나와요. // web/src/lib/course/lib.ts const sides = waypointCount + 1; // 경유지 2개 + 출발점 = 정삼각형 const radius = targetDistanceM / detourFactor / (2 * sides * Math.sin(Math.PI / sides)); const center = destinationPoint(start, headingDeg, radius); 거리가 안 맞으면 딱 한 번만 다시 동네마다 길 모양이 달라서 k=1.3이 늘 맞진 않아요. 그래서 받아온 경로가 목표 ±10% 밖이면 k를 √(실제/목표)만큼 고쳐서 한 번만 다시 요청해요. 제곱근을 씌운 건 한 번에 너무 크게 고치지 않으려고요. 재시도는 한 번까지만 해요. 무료 API 호출 수가 정해져 있어서 정확도를 조금 내주고 호출 수를 아꼈어요. 대신 처음 받은 경로와 재시도 경로를 둘 다 후보로 남겨서 고를 때 같이 비교해요. 📊 세 코스는 이렇게 골라요 후보가 모이면 코스마다 러너가 신경 쓰는 걸 숫자로 뽑아요. 항목 어떻게 세나 횡단보도·계단 TMAP 응답의 turnType 코드 (횡단보도 211~217, 계단 127·129) 오르막 고도가 기준점보다 2m 이상 바뀌어야 오르막으로 인정 (고도 데이터 잡음 거르기) 겹침 30m 격자 기준으로 같은 길을 다시 지나간 비율 예상 시간 거리 × 내 페이스 (TMAP 소요 시간은 걷기 기준이라 안 써요) 이걸 가중치를 곱해 더한 점수로 바꾸고(낮을수록 좋은 코스) 역할별로 하나씩 뽑아요. 1 종합 점수 1등, 2 오르막이 제일 적은 코스, 3 횡단보도가 제일 적은 코스예요. 같은 코스가 두 군데서 1등을 하면 앞 슬롯이 가져가고 뒤 슬롯에는 2등을 보여주면서 그 이유를 한 줄 적어둬요. 경사는 지도 타일 "이미지"에서 읽어요 고도는 AWS Terrain Tiles를 써요. 처음엔 Open-Meteo 고도 API를 썼는데 요청 한도(429)에 자꾸 걸려서 바꿨어요. Terrain Tiles는 키도 한도도 없고 고도가 PNG 이미지의 RGB 값 에 들어 있어요. // Terrarium PNG 한 픽셀 → 고도(m) elevation = R * 256 + G + B / 256 - 32768; z13 타일 한 장이 약 3.9km 범위라서 한 장 받아두면 주변 코스를 거의 다 처리할 수 있어요. 캐시 효율이 좋아요. 코스를 100m 간격으로 찍어서 고도를 읽고 구간마다 경사율로 색을 칠해요. 1%까지는 평지, 3%부터 완만, 6% 넘으면 언덕이에요. 고도 차트를 누르면 그 지점이 지도에 표시돼요. 마음에 드는 코스는 '내 코스'에 저장해 둘 수 있어요 💸 월 0원으로 굴리기 개인 프로젝트라 운영비 0원 을 처음부터 조건으로 잡았어요. 구성 쓴 것 서버·배포 Next.js Route Handler + Vercel Hobby 경로 TMAP 보행자 API (무료 하루 1,000건) 고도 AWS Terrain Tiles (키·한도 없음) 저장 로그인 없이 브라우저 localStorage 걸리는 건 TMAP 한도예요. 코스 요청 한 번에 TMAP을 6~10번 부르거든요. 그래서 IP당 10분에 6번으로 요청을 막아두고 한반도 밖 좌표처럼 말이 안 되는 입력은 API를 부르기 전에 걸러요. 아쉬운 점도 있어요. 공개 고도 데이터라 도심에서는 건물이나 고가도로가 오르막처럼 잡히기도 해요. 평지에 고도 오차만 섞어 시뮬레이션해 봤더니 5km에 수십 m짜리 가짜 오르막이 나오더라고요. 그래서 절대 숫자 대신 평지 / 완만 / 언덕 등급으로만 보여줘요. 신호 데이터도 없어서 "신호등 N개"가 아니라 "횡단보도 N개"라고 적었어요. 🙌 한번 써보세요 "둔산동 런닝 코스"로 검색해도 안 나오던 동네 코스, 이제는 출발점이랑 5km만 넣으면 바로 3개가 나와요. 한국 지역만 되고 모바일 화면 기준이에요. 여러분 동네에서도 한번 돌려보시고 이상한 코스가 나오면 알려주세요 🙂 👉 루프런 써보기 — loop-run-eight.vercel.app
도넛런 빌드 일지 (7화) — 현실을 배운 달 7화는 8월 얘기다. 7월이 제품이 세상에 나오는 달 이었다면, 8월은 현실이 우리를 가르치는 달 이었다. 배운 게 많다. 아팠던 것도 있고, 웃긴 것도 있다. 하나씩 정리해본다. 7월 31일 ~ 8월 2일, 팬타포트 락페에 갔다. 원래 계획은 우리 바람막이가 나오면 그걸 입고 가는 거였다. 근데 첫째, 바람막이는 아직 개발 중이었고. 둘째, 인천은 너무 더웠다. 어차피 못 입었을 거다. 그래서 그냥 갔다. 락페를 락페로 즐기려고. 근데 도착하고 얼마 안 지나서 이상한 걸 발견했다. 사람들이 러닝웨어를 패션으로 입고 왔다. 특히 살로몬이 눈에 띄었다. 한둘이 아니었다. 아저씨 아줌마부터 20대 초반까지 세대와 상관없이 많았다. 러닝 트랙에서 볼 법한 브랜드가 락페 관객석에 앉아 있었다. 돌아오는 길에 생각했다. 기능성을 챙기면서 브랜드 목소리와 아이덴티티가 뚜렷하면, 러닝웨어는 러닝을 넘어설 수 있겠다. 우리가 하려는 방향과 결이 맞았다. 러닝을 잘 뛰기 위한 옷이지만, 그 이상의 무드가 있는 옷. 러닝하지 않는 날에도 입고 싶은 옷. 살로몬의 무드는 우리와 다르다. 걔네는 트레일이고 아웃도어다. 우리는 도시의 러너다. 근데 자기 무드를 뾰족하게 세운다는 관점에서는 배울 게 많다. 락페에서 브랜드 힌트를 얻어올 줄은 몰랐다. 역할이 정리됐다 락페 다녀오고 나서, 이혜선 공동대표와 역할을 다시 정리했다. 그전까지는 제조사와의 소통을 내가 맡고 있었다. 근데 이걸 이혜선 대표에게 완전히 넘겼다. 이유는 단순했다. 이혜선 대표가 나보다 훨씬 잘한다. 제품 감각, 원단 감각, 공장 커뮤니케이션의 결. 셋 다 나보다 낫다. 나는 흉내를 냈던 거고, 이혜선 대표는 원래부터 그 언어를 하는 사람이다. 내가 맡게 된 건 행정, 재무, 홈페이지, 광고, 물류, 데이터. 이건 내가 좋아하고 잘할 수 있는 영역이다. 공동창업의 첫 번째 원칙 — 잘하는 사람이 하는 게 회사 전체에 좋다. 역할이 정리되고 나서 회사가 훨씬 빨라졌다. 서로의 영역을 침범하지 않으니 결정도 빠르고, 결과도 좋다. 진작 이랬어야 한다. 8월 9일, 오더 0의 날 그전까지는 매일 최소 한 건씩은 주문이 있었다. 큰 숫자는 아니지만, 하루도 빠지지 않았다. 8월 9일부터 며칠간 그 매일의 한 건이 0이 됐다. 처음 있는 일이었다. 원인이 뭘까 한참 생각했다. 광고 세팅을 잘못 건드렸나. 홈페이지가 이상한가. 뭘 놓쳤나. 한참 뒤에야 깨달았다. 8월 8일이 갑자기 추워진 날이었다. 여름 러닝 싱글렛을 파는 브랜드에게 여름의 서늘한 하루는 곧 매출 0이었다. 우리가 뭘 잘못한 게 아니었다. 날씨였다. 의류 비즈니스가 날씨에 예민하다는 얘기는 많이 들었다. 근데 직접 겪어보니까 겁이 났다. 우리가 제어할 수 없는 것 하나가 이렇게 크게 매출을 흔든다는 거. 이건 이론으로는 안 배워진다. 그리고 이 경험 하나로 우리의 15개월 계획이 바뀌었다. 여름 상품에만 의존하면 안 된다. 사계절 팔릴 수 있는 라인업이 필요하다. 날씨 하나가 15개월 로드맵을 만들었다. 8월 11일, 엑심베이 이 즈음 해외 판매를 준비하고 있었다. 우리 글로벌 사이트(donut-run.com)는 이미 있었다. 근데 결제가 페이팔 하나뿐이었다. 페이팔이 요즘 세대에게 얼마나 낯선지 다들 알 거다. 그래서 엑심베이를 신청했다. 국내 PG인데 해외 카드도 받는 곳이다. 8월 11일 신청, 8월 27일 카드사 승인. 이 사이 16일이 어떻게 지나갔는지 기억이 안 난다. 서류 준비하고, 심사 대응하고, 카드사 개별 승인 기다리고. 창업 초기 이런 것들은 하나하나가 다 배움인데, 지나고 보면 그저 시간이 걸렸다는 기억뿐이다. 승인이 나던 날, 실제로 테스트 결제를 해봤다. 신용카드 정보를 입력하고, 결제 완료 화면을 봤다. 이제 진짜 글로벌 브랜드다. 이 문장 하나가 며칠간 남아 있었다. 8월 13일, 두 가지 일이 같은 날 있었다 이날은 좀 특별하게 남았다. 오전 — 강원대 창업 공간 프레젠테이션 강원대 보듬관에서 프레젠테이션을 했다. 창업 공간 입주를 위한 자리였다. 준비를 열심히 했다. 자료도 신경 써서 만들었고, 발표도 여러 번 연습했다. 결과적으로 떨어졌다. 사실 우리는 아직 공간을 가지기엔 부담이 컸다. 매달 나가는 고정비가 무거워질 수 있었다. 그래서 되면 좋고, 안 되어도 어쩔 수 없다는 마음이었다. 근데 막상 떨어지고 나니 좀 씁쓸했다. 떨어진 이유가 뭐였는지 정확히 모른다. 요즘 창업 지원 사업들이 다 그렇다. 심사 결과만 오지 왜 떨어졌는지는 안 알려준다. 그래서 배우기가 어렵다. 다음번엔 뭘 고쳐야 할지 알 수 없으니까. 오후 — 25km 러닝 씁쓸한 마음으로 오후를 보낼 수 없었다. 우리 옷을 입고 25km를 뛰기로 했다. 춘천 마라톤 코스와 거의 비슷한 루트다. 신매대교까지 갔다가 돌아오는 길. 25km 정도. 에어 라이트를 입었다. 우리가 만든 가장 가벼운 싱글렛이다. 뛰기 시작하고 얼마 지나지 않아 땀이 났다. 그런데 이상하게 무겁게 느껴지지 않았다. 원래 여름 러닝은 옷이 땀에 젖으면 몸에 착 감기면서 불편해진다. 근데 에어 라이트는 땀이 나자마자 마르는 느낌이었다. 마르는 게 아니라 애초에 머금지 않는 것 같았다. 25km를 다 뛰고 나서 옷을 보니, 젖어 있긴 했지만 축축한 느낌이 아니었다. 벗어서 쥐어짤 정도로 젖지도 않았다. 우리가 만든 옷을 우리가 입고 확인했다는 것 — 이날 이 감각이 오래 남았다. 강원대에서 떨어진 오전과, 우리 옷의 실물 성능을 몸으로 확인한 오후. 같은 날에 겹쳐 있었다. 돌아오는 길에 이런 생각이 들었다. 제도가 우리를 인정하지 않아도, 우리 제품은 우리 것이다. 그리고 우리 제품은 제 몫을 한다. 이날 이후로 창업 지원 사업에 대한 무게가 좀 가벼워졌다. 되면 좋고, 안 되면 어쩔 수 없다. 우리가 진짜 붙잡아야 할 건 제품이다. 8월 중순, 해외 판매 시작 엑심베이가 살아났으니 이제 진짜 해외 판매를 시작할 수 있었다. 주 타겟은 홍콩과 싱가포르였다. 이유는 단순했다. 거긴 사계절 여름에 가깝고, 아시아권이라 배송비도 감당할 만했다. 의외로 잘 팔렸다. 한국 러닝 브랜드에 대한 관심이 있는 건지, 우리 미학이 통한 건지, 아니면 그냥 소재가 가벼워서인지. 정확한 이유는 모르겠다. 근데 확실한 건 하나 있다. 국내가 죽는 시즌에 해외가 사는 시즌이 있다. 이걸 어떻게 활용할지가 우리 겨울 전략의 핵심이 될 거다. 8월 30일, 상세페이지 리뉴얼 오더가 줄어든 김에 홈페이지 상세페이지를 대폭 손봤다. 원래 상세페이지가 너무 스펙 위주였다. 원단 정보, 무게, 케어 지침. 정보는 있지만 감정이 없었다. 새 상세페이지는 왜 이 옷을 만들었는지, 어떻게 입으면 좋을지 를 중심으로 다시 짰다. 스펙은 밑에 두고, 위엔 스토리를 뒀다. 결과가 즉시 왔다. 세션이 두 배로 늘었다. 결제 시작도 두 배가 됐다. 리뉴얼 하나로 이렇게 바뀔 수 있다는 게 놀라웠다. 같은 트래픽에서도 상세페이지 하나가 매출을 두 배로 만든다. 이건 이론이 아니라 우리 데이터로 확인했다. 앞으로 상세페이지는 계속 손볼 거다. 리뉴얼이 아니라 상시 튜닝. 재고가 어긋났다 8월 중순부터 이상한 신호가 들어왔다. 완전 품절 제품이 나오기 시작했다. 우리는 발주하기 전에 쓰레드에서 선호도 조사를 했다. 컬러 두 개 중 어떤 게 좋은지, 어떤 사이즈가 잘 팔릴지. 900명 넘게 답을 주셨다. 그 데이터를 바탕으로 재고를 잡았다. 결과는 정반대였다. 여성분들의 호응이 강해서 여성 재고를 잔뜩 잡았다. → 실제 판매는 남녀 비슷했다. 클리어 스카이(하늘색)에 호응이 몰렸다. → 실제 판매도 하늘색이 더 나갔지만, 호응 비율만큼은 아니었다. 남성 M·L 사이즈가 훨씬 빠르게 소진됐다. 한마디로 정리하면 이거다. 호응과 구매는 다르다. 호응은 감정이다. 구매는 결단이다. 500원짜리 스티커에 좋아요를 누르는 것과, 49,000원짜리 옷에 결제 버튼을 누르는 건 완전히 다른 행위다. 우리가 데이터를 보는 방식이 틀렸다. 좋아요와 댓글은 방향성을 알려주지만, 실판매 예측에는 부족한 신호다. 다음 발주부터는 실판매 소진율을 기준으로 잡기로 했다. 감정이 아니라 결단의 흔적을 따라가기로. 첫 번째 큰 수업료였다. 모두의 창업 최종 보고서 8월 말에는 모두의 창업 최종 보고서를 제출했다. 멘토링부터 시작해서 몇 개월간의 여정을 정리한 보고서였다. 무엇을 배웠고, 무엇을 바꿨고, 어디로 가는지. 결과적으로 2라운드에는 진출하지 못했다. 이번엔 이유를 좀 알 것 같다. 우리 사업이 가진 임팩트를 심사자들에게 충분히 전달하지 못했다. 우리한테는 명확하지만, 처음 보는 사람에게는 어려웠던 부분이 있었을 거다. 그래서 2차에 다시 지원할 계획이다. 이번엔 변환형 바람막이로 간다. 이 제품 하나가 우리 브랜드의 정체성과 기술력을 동시에 보여줄 수 있는 카드다. 8월의 마무리 8월은 이런 달이었다. 락페에서 브랜드 힌트를 얻어왔다 역할을 정리해서 회사가 빨라졌다 날씨 하나가 로드맵을 바꿨다 엑심베이로 진짜 글로벌 브랜드가 됐다 강원대에서 떨어졌지만 우리 제품은 확인했다 해외 판매가 시작됐다 상세페이지 리뉴얼로 매출이 두 배가 됐다 재고 예측이 틀렸다는 걸 데이터로 배웠다 한 달에 참 많은 걸 배웠다. 좋은 것도 아픈 것도 다 배웠다. 이걸 다 겪어야 다음 시즌이 가벼워질 거다. 다음 편 다음 편(8화)은 9월 얘기와 앞으로의 그림이다. 완만한 회복과 단체 주문 첫 교환의 순간 이혜선 공동대표 체제 정착 하반기부터 내년까지 15개월 계획 그리고 못 잊는 그 아쉬움 — 첫 제품이 5월에 나왔다면 같이 가자. 아자.
들어가기 앞서 오늘도 개인프로젝트의 주제를 구상하였고, 이후 주제에서 수정할부분이나 추가할 부분을 스스로 개인프로젝트 주제 청년 지원 사업 및 자격증 준비 챗봇 청년 지원 사업 및 자격증 준비 챗봇 왜 청년지원사업과 자격증 준비를 하나로 합쳤는가? 청년 지원사업을 찾아보는 연령대가 보통 청년대인데 이때 자격증 준비도 같이 하는 경우가 많아 별개의 기능으로 넣음 근데 청년 지원사업이면 사람인 같은 구직사이트에 api도 필요한가? 아니라고 생각함 ᄂ 근데 청년 지원 사업에 해당하는 기업같은건 어떡하지? → 근데 이것도 api안에 존재할 것임 주요기능은? 챗봇으로 입력받고 해당 사람이 받을 수 있는 지원 사업이 무엇인지 확인하는게 목표 수집방식? 이게 페이지수와 페이지 사이즈로 전체 불러올때까지 반복할 예정인데 잘 될지는 모르겠음 평가방식은? 말그대로 데이터에 있을법한 말 20개와 애매한 5개 오답 5개를 넣어 챗봇이 얼마나 맞출 수 있냐 체크 ᄂ 정답먼저 20개 만들기 문제 / 정답 / 근거위치 순 ᄂ 평가셋의 종류는 일단 일부만 진행 → 5~10개정도 ᄂ 10개면 청년정책 3 자격증관련 3 애매한거 2 오답 2로 진행 평가를 진행할때 GPT한테 물어봐서 답이 다 나오면 의미없음 → GPT도 알 정보면 이미 공공정보일거라 고려사항 예시) 정보처리기사의 필기 합격기준이 뭔지 알려줘 모든 과목 40 이상 , 총 평균 60 이상 / (정보처리기사, 기사 총 점수) ᄂ 근데 이건 인터넷 검색만 해도 바로 나오는 정보지 않나? → 진짜로 api로만 해서 나올 수 있게 ᄂ 예시) 19~29세 사이에 성인 여성이 받을수 있는 정책 ← 정답 근데 청년 지원 사업은 GPT도 잘 모를텐데 자격증 준비는 누구나 다 아는거 아닌가? → 그럼 의미 없는거 아닌가? 예외 사항이 발생한만한가? 정책중에 꼭 청년이라고 19 35세가 아닌 19 39세 등 청년이라 해당하는 사항 이상의 나이를 받은 경우 처리방법에 대해 생각해봐야함 ᄂ 그리고 제한없음은? → 이건 그냥 청년만 쓰게 설계된거니까 상관없다 판단함 청킹 방식은 어떻게 할것인가? 어떻게 나눌 것인가? → api라 기존 청킹으로 나누기에는 무리가 있어보임 → 정책 하나당 청킹 하나로 진행 → 청킹 많이 만들어져도 상관 X ᄂ 메타데이터는 어떤건가? → 정책 하나에 들어갈 데이터들의 필터들 필터링용 데이터들(식별용, 사용자 조건 필터용, 기간 필터용, 분야 필터용) plcyNm 정책명 sprtTrgtMinAge, sprtTrgtMaxAge, sprtTrgtAgeLmtYn 나이 필터 zipCd 지역 필터 earnCndSeCd, earnMinAmt, earnMaxAmt 소득 필터 mrgSttsCd 결혼 여부 jobCd 취업 상태 (재직/미취업 등) schoolCd 학력 plcyMajorCd 전공 sBizCd 특화 요건 (군인, 중소기업 등) aplyPrdSeCd, aplyYmd 신청기간 (상시 여부, 마감일) bizPrdSeCd, bizPrdBgngYmd, bizPrdEndYmd 사업기간 lclsfNm, mclsfNm 분야별 필터 (본문과 중복 저장) 그럼 데이터 정제는 어떻게 처리 할 것인가? 정제 규칙은 무엇으로 할것인가? → 어떤걸 남기고 어떤걸 지울것인가? 남길 데이터 plcyNm 정책명 plcyExplnCn 정책설명 plcySprtCn 지원내용 (가장 중요) plcyKywdNm 키워드 (검색 적중률 향상) lclsfNm, mclsfNm 대분류·중분류명 sprvsnInstCdNm 주관기관명 ("어디서 하는 정책이야?" 대응) operInstCdNm 운영기관명 plcyAplyMthdCn 신청방법 srngMthdCn 심사방법 sbmsnDcmntCn 제출서류 addAplyQlfcCndCn 추가 신청자격 조건 ptcpPrpTrgtCn 참여 제한 대상 earnEtcCn 소득 기타 조건 bizPrdEtcCn 사업기간 기타 내용 etcMttrCn 기타사항 sprtSclCnt 지원규모 (예: 100명) 필터링용 데이터 sprtTrgtMinAge, sprtTrgtMaxAge, sprtTrgtAgeLmtYn 나이 필터 zipCd 지역 필터 earnCndSeCd, earnMinAmt, earnMaxAmt 소득 필터 mrgSttsCd 결혼 여부 jobCd 취업 상태 (재직/미취업 등) schoolCd 학력 plcyMajorCd 전공 sBizCd 특화 요건 (군인, 중소기업 등) aplyPrdSeCd, aplyYmd 신청기간 (상시 여부, 마감일) bizPrdSeCd, bizPrdBgngYmd, bizPrdEndYmd 사업기간 lclsfNm, mclsfNm 분야별 필터 (본문과 중복 저장) sprtArvlSeqYn 선착순 여부 plcyAprvSttsCd 승인 상태 (승인된 정책만 서비스) 표면적으로 남길 데이터 → 화면에 보여주지 않음 plcyNo 정책 고유 ID, 업데이트 기준 lastMdfcnDt 변경 감지 (이 값이 바뀐 정책만 다시 임베딩) frstRegDt "새로 나온 정책" 기능용 (선택) aplyUrlAddr 답변 끝에 신청 링크로 제공 refUrlAddr1, refUrlAddr2 참고 링크로 제공 inqCnt 인기 정책 정렬용 (선택) 삭제할 데이터 bscPlanCycl, bscPlanPlcyWayNo, bscPlanFcsAsmtNo, bscPlanAsmtNo 정부 기본계획 내부 분류번호 pvsnInstGroupCd, plcyPvsnMthdCd 내부 관리 코드 sprvsnInstCd, operInstCd 기관 코드 (기관명만 있으면 충분) sprvsnInstPicNm, operInstPicNm 담당자 개인 이름 (개인정보라 제외) rgtrInstCd ~ rgtrHghrkInstCdNm (6개) 등록자 기관 정보, 사용자에게 무의미 자격증 데이터 청킹 <contents> 정보처리기사 출제경향 <실기시험 출제 경향> 정보시스템 등의 개발 요구 사항을 이해하여 각 업무에 맞는 소프트웨어의 기능에 관한 설계, ... 출제기준 참조(www.q-net.or.kr) </contents> <contents> 정보처리기사 취득방법 시행처 : 한국산업인력공단 관련학과 :모든 학과 응시가능 시험과목 - 필기 ... 실기 : 100점을 만점으로 하여 60점 이상. </contents> 자격증 관련 데이터 정제 정제할 데이터 <contents> 정보처리기사 출제경향 <실기시험 출제 경향> 정보시스템 등의 개발 요구 사항을 이해하여 각 업무에 맞는 소프트웨어의 기능에 관한 설계, ... 출제기준 참조(www.q-net.or.kr) </contents> <contents> 정보처리기사 취득방법 시행처 : 한국산업인력공단 관련학과 :모든 학과 응시가능 시험과목 - 필기 ... 실기 : 100점을 만점으로 하여 60점 이상. </contents> 필터링 데이터 <infogb>출제경향</infogb> <jmfldnm>정보처리기사</jmfldnm> <infogb>취득방법</infogb> <jmfldnm>정보처리기사</jmfldnm> <mdobligfldnm>정보기술</mdobligfldnm> <obligfldnm>정보통신</obligfldnm> 삭제할 데이터 <mdobligfldcd>211</mdobligfldcd> <obligfldcd>21</obligfldcd> <item> <contents> 정보처리기사 출제기준 정보처리기사 출제기준입니다. 메뉴상단 고객지원-자료실-출제기준 에서도 보실 수 있습니다. </contents> <infogb>출제기준</infogb> <jmfldnm>정보처리기사</jmfldnm> <mdobligfldcd>211</mdobligfldcd> <mdobligfldnm>정보기술</mdobligfldnm> <obligfldcd>21</obligfldcd> <obligfldnm>정보통신</obligfldnm> </item> <response> <header> <resultCode>00</resultCode> <resultMsg>NORMAL SERVICE.</resultMsg> </header> </response> 오늘 후기 오늘도 주제 구상을 진행하였습니다. 이번에는 주제를 구상하면서 저에게 의문이 생겨났습니다. 제가 하고 있는 이 챗봇에서 과연 자격증 관련 챗봇이 필요한가. 원래 저의 의도는 보통 청년 정책을 찾는 사람은 청년이고, 이 챗봇을 찾는 사람에게 조금이라도 도움을 드리고자 자격증 관련도 넣으면 좋겠다 에서 시작하였는데, 막상 다시와 생각해보니 자격증 관련 정책들은 GPT나 타 AI에게도 학습되어 있을 확률이 높아 주제에서 제외해야할지 고민하게 되었습니다.