약 4주간의 알고리즘 주차를 마치고, C언어를 학습하고 활용하는 주차에 들어서게 되었다. 이번 6주차에는 C언어 기초문법을 간단히 학습하고, 이를 활용하여 기본 자료구조를 구현하는 문제들을 푸는것이 주요 과제였다. 또한 C언어의 핵심 개념인 포인터와 동적 메모리 할당 메서드 'Malloc'을 공부해보면서 C언어에 익숙해지는 과정을 거쳤다.
남은 4개월 동안 알고리즘 문제를 풀 일이 없을텐데, 이러면 수료후에 다시 알고리즘 문제를 못 풀게 되는것이 아닌지 걱정이 된다. 비록 남은 교육과정이 쉽지만은 않겠지만, 일주일에 2~3문제는 백준/LeetCode 문제를 푸는 습관을 들여보도록 하자... ㅎㅎ
핵심 역량 평가
| 역량 | 달성도 | 목표 |
| 문제해결 | 100% | LL, Stack and Queue, BT, BST 각 개념당 최소 4문제 이상 해결 |
| 모든 문제를 해결하였다. | ||
| 설계 | 50% | 수요코딩회에서 하드코딩의 빈도를 줄이고, 확장성을 최대한 고려한다. |
| 시간 부족으로 인해 코드의 몇몇 부분에서 하드코딩을 한 부분들이 존재했다. | ||
| 구현 / 품질 | 100% | 과제물에 대해 의도한 로직대로 수행되는 코드를 짜고, 다양한 테스트케이스 입력을 해보며 결과물을 검증한다. |
| 모든 문제에 대해 예외상황까지 철저히 고려한 테스트케이스를 통해 정답을 검증하였다. | ||
| 유지보수 | 80% | 가독성 좋은 코드를 작성한다. |
| 변수명이나, 코드의 흐름이 직관적으로 보이게끔 코드를 짜려고 노력했다. | ||
| 협업 | 70% | 모르는 문제에 대해 해결하면, 혼자 넘어가지 않고 팀원들과 해당 지식을 공유한다. |
| 포인터와 Malloc 회의, 수요코딩회에서는 팀원들과 학습한 내용을 최대한 공유하였지만, 과제에 대해서는 그렇게 하지 못했다. | ||
| 태도 | 80% | 이해가 모호한 개념에 대해 절대로 그냥 넘어가지 않는다. |
| 포인터와 Malloc 개념, 그리고 수요코딩회에서 산출된 코드들을 대충 알고 넘어가는 것이 아닌, 깊은 이해를 하려고 노력했다. | ||
| 비즈니스 이해 | 80% | 수요코딩회에서 다른 사람이 봐도 이해가 명확히 될만한 구성으로 설계를 한다. |
| 가독성을 살리기 위해 pretty table로 터미널 출력 내용을 보기 좋게 하였다. | ||
| AI 활용 | 90% | AI를 통해 꼬리 질문을 거듭하며 모호한 개념에 대한 이해와 응용력을 극대화한다. |
| AI를 활용한 학습을 할 때는, 깊은 학습을 위한 꼬리 질문을 반복하며 문제 및 개념에 대해 확실히 이해하려고 노력하였다. | ||
| 학습 민첩성 | 90% | C언어 문법과 포인터/Malloc 개념을 빠르게 습득하여 문제에 적용해본다. |
| 이중 포인터 내용을 깊게 학습하고, 링크드 리스트 과제에서 적절히 활용하였다. | ||
이번 주 핵심 개념 - 포인터와 Malloc
사실 이번 주차의 핵심 주제는 C언어를 활용한 자료구조 구현이라고 보는 것이 더 적절하다. 그러나 대부분의 문제는 포인터와 동적 할당에 대한 이해 없이는 해결하기 어려웠다. 이에 따라, 이번 주차에 해당 개념들을 확실히 정리해두는 것이 이후 C언어 학습과 Pintos 프로젝트 진행에 큰 도움이 될 것이라 판단하였다. 따라서 기존에 개념적으로만 알고 있던 내용들을 보다 깊이 있게 추가 학습하였다.
이중 포인터 활용
값 복사(pass by value)
function(x);
C언어에서는 함수를 호출할 때 인자 자체가 전달되는게 아니라, 인자의 값이 복사돼서 들어간다.
#include <stdio.h>
void f(int *t) {
t = NULL;
}
int main() {
int x = 10;
int *p = &x;
f(p);
// %p는 반드시 void* 타입을 요구
printf("%p\n", (void*)p);
}
코드에서 f(p)를 호출할 때,
int *p = &x; // 주소값 복사
이런식으로 p 포인터 변수에 x의 주소값을 복사한다.
하지만 이는 주소값이 복사됐을뿐 실제 p와는 다른 객체이다.
즉 똑같이 &x를 가리킬 뿐, 함수 호출할때 인자로 넘긴 p와 함수 내에서 사용하는 t는 전혀 다른 객체라는 뜻이다.

따라서 포인터 변수 t가 NULL을 가리키게 해도, 기존 포인터 변수 p는 그대로 x의 주소를 가리키고 있기 때문에, printf시 NULL이 아닌 &x 값이 그대로 출력된다.
#include <stdio.h>
void f(int *t) {
*t = 20; // 가리키는 대상 변경
}
int main() {
int x = 10;
int *p = &x;
f(p);
printf("%d\n", x);
}
만약 포인터 변수 t 자체가 아니라, *t로 역참조하여 값을 바꾸면 어떻게 될까?
t가 가리키는 주소, 즉 x가 저장된 메모리를 직접 수정하기 때문에, printf 시 10이 아닌 변경된 값 20이 출력된다.
포인터 자체를 바꾸는 법
위 코드에서 함수의 인자로 넘기는 *p가 수정되어 NULL을 가리키게 하려면 어떻게 하면 될까?
#include <stdio.h>
void f(int **t) {
*t = NULL;
}
int main() {
int x = 10;
int *p = &x;
f(&p);
// %p는 반드시 void* 타입을 요구
printf("%p\n", (void*)p);
}
이전 코드와 달리 f 함수는 인자를 이중 포인터(int **)로 받고 있다. 호출 시 &p를 전달하므로, t는 p 자체의 주소를 가리키게 된다. 따라서 *t로 역참조하면 p가 저장된 메모리에 직접 접근할 수 있고, *t = NULL은 p의 값을 NULL로 덮어쓴다. 결과적으로 printf에서는 x의 주소가 아닌 NULL이 출력된다.

Malloc
실행 중(runtime)에 필요한 크기만큼 메모리를 힙(heap) 영역에서 할당받는 함수이다.
void *malloc(size_t size);
- size 바이트만큼 메모리를 요청
- 성공하면 그 메모리의 시작 주소를 반환하고, 실패하면 NULL 반환
- 할당한 공간에 어떤 자료형이 들어오는지 모르기 때문에, 범용 포인터 타입인 void* 를 반환한다.
💡void* 포인터
아직 타입이 정해지지 않은 주소이다.
-> 즉 주소는 들고 있지만, 그 주소를 어떤 타입으로 읽어야 할지는 아직 모르는 상태이다.
컴파일러는 void* 타입의 변수를 보고 그 주소의 메모리를 어떤 자료형으로 해석해야 할지 알 수 없기 때문에
역참조가 불가능하다.
void *vp = malloc(sizeof(int));
*vp = 10; <-- 불가능
메모리 누수
void f() {
int *p = malloc(sizeof(int));
}
함수가 끝나면 p라는 포인터 변수는 사리지지만, 힙에 만든 공간은 그대로 남아 있을 수 있다.
- 그 주소를 잃어버리면 더 이상 접근도 못 하고 해제도 못한다.
위 코드에서 p라는 변수 자체는 스택에 저장되고, p 안의 값은 힙 메모리에 저장된다.
free(p)
이건 p라는 변수 자체를 지우는 것이 아니다.
p가 가리키고 있는 힙 메모리를 운영체제/메모리 할당기에게 반납하는 것이다.
➡️하지만 포인터 변수 p와, 그 안에 있던 힙 메모리 주소값은 남아있을 수 있다.
free 전
- p : "0x500"
- 0x500 : 사용자가 쓰는 유효한 힙 메모리
free 후
- p : 여전히 "px500"
- 하지만 0x500은 이제 메모리 할당기에게 반납한 유효하지 않은 공간
이와 같은 상황에서 p 같은 포인터를 댕글링 포인터(dangling pointer)라고 한다.
이를 방지 하기 위해서는...
free(p);
p = NULL;
이와 같이 p에 저장된 옛 주소를 초기화해주는 습관을 들이는 것이 좋다.
❓ free 후 값을 읽으면 어떻게 되는가
int *p = malloc(sizeof(int));
*p = 10;
free(p);
printf("%d\n", *p); // 위험
이를 정의되지 않은 동작(undefined behavior) 이라고 한다.
- 우연히 10이 출력될 수도 있음
- 이상한 쓰레기값이 나올수도 있음
- 프로그램이 죽을 수도 있음
보통 malloc/free는 직접 운영체제랑 매번 1:1로 일하는 게 아니라, 중간의 메모리 할당기(allocator)가 관리한다.
- 프로그램이 malloc 요청
- 메모리 할당기가 큰 힙 영역 안에서 적당한 블록을 하나 할당
- free 하면 그 블록을 "재사용 가능" 상태로 표시
여기서 블록을 재사용 가능으로 전환할때 초기화를 보장하지 않는다.
그렇기 때문에 malloc 직후에 값을 바로 읽으면 이전에 저장되었던 쓰레기값을 읽어올 수 있다.
따라서 malloc 직후에 값을 바로 읽지 말고 바로 초기화를 하거나 calloc을 쓰는것이 안전하다.
수요 코딩회
https://github.com/Developer-EJ/SQL-Parser
이번 주 수요 코딩회 주제는 INSERT와 SELECT 기능을 제공하는 간단한 SQL Parser를 만드는 것이었다.
로직 자체가 매우 복잡하거나 어려운 것은 아니었지만, 모든 로직을 다 이해하고 있어야 설명이 가능한 과제였기 때문에 쉽지만은 않았던 것 같다.
매주 수요코딩회의 진행 양상이 점점 비슷해지고 있는 것 같아서 고민을 해봤다. 매주 팀프로젝트를 하는 것은 흔하지 않은 기회이기 때문에 새로운 것을 도전해보는 것도 좋을 것이라고 생각했다. 그래서 Agents.md 파일에 모든 협업 규칙을 넣어두는 기존 방식과는 다른 새로운 방법을 적용시켜보기로 했다. 특히, 최근 자주 언급되는 '하네스 엔지니어링'에 대해 공부해보았고 프로젝트에 적용시켜보았다.
2026.04.09 - [AI] - [기초] 하네스 엔지니어링이란?
[기초] 하네스 엔지니어링이란?
https://www.youtube.com/watch?v=DrekqeDlO1w 해당 영상을 보고 공부한 내용을 간략하게 요약해 두려고 한다. 하네스 엔지니어링이란?에이전트가 실수를 할 때마다, 그 실수가 다시는 반복되지 않도록 환경
dev-ej.tistory.com
하네스 엔지니어링이 무엇인지에 대해 간략하게 글로 정리해두었다. 🕶️
AI 활용 전략
| 계층적 컨텍스트 분리 | src/*/CLUADE.md | AI 역할 격리 |
| 에이전트 경계 강제 | CLAUDE.md 규칙 | 모듈 충돌 방지 |
| 인터페이스 계약 | include/interface.h | 공유 상태 최소화 |
| 하네스(Harness) | .githooks/pre-commit (커밋 시점) + PostToolUse (수정 즉시) |
AI 출력 자동 검증 |
| 피드백 루프 기억 | REVIEW_RULES.md | 실수 패턴 누적 차단 |
| 스펙 주도 생성 | tests/TEST_SPEC.md | AI 생성 방향 명세 |
이 프로젝트는 AI가 협업할 때 발생할 수 있는 문제를 구조적으로 막는 여섯 가지 기법을 사용했다.
먼저 src/*/CLAUDE.md로 컨텍스트를 계층 분리해 각 AI가 자기 역할에만 집중하게 했고, CLAUDE.md 규칙으로 모듈 간 코드 충돌을 원천 차단했다. 여기서 루트에 있는 CLAUDE.md 파일은 프로젝트의 대략적인 흐름만 기재해두었고, 실제 기능폴더마다 들어있는 CLAUDE.md 파일에 각 기능에 대한 요구사항을 정의해두었다.
include/interface.h를 유일한 통신 채널로 고정해 공유 상태를 최소화했으며, pre-commit hook과 lint.sh로 구성된 하네스가 AI가 생성한 코드를 코드 수정 및 커밋 시점에 자동 검증해 잘못된 코드가 저장소에 들어오지 못하게 막았다.
여기에 더해 REVIEW_RULES.md에 AI가 저지른 실수 패턴을 누적 기록해 같은 문제가 반복되지 않도록 했고, TEST_SPEC.md로 AI가 만들어야 할 결과물을 체크리스트 형태로 미리 명세해 생성 방향을 통제했다.
느낀점

팀원 모두가 같은 수준의 이해를 하는것을 목표로 하여 구현에 욕심을 부리진 않았다.
오전 내내 개인 공부와 설계를 중점적으로 다루었고, 실제 구현은 2시간도 채 걸리지 않았다.
각자 맡은 부분의 코드를 공부하여 서로 발표하는 시간을 가졌고, AI 사용 방식에 대해서도 다양한 대화를 나누었다.
특히, Agent의 차이로 인해 협업 과정에서 문제가 될 수도 있다는 점이 놀라웠다.
클로드에서 지정한 규칙을 Codex쪽에서 마음대로 우회하여 진행시키는 것을 보고 설계 단계에서 조금 더 시간을 들이는 것도 좋았을 것이라는 생각이 든다.. ㅋㅋ
비록 결과물이 화려하거나 차별점이 있지는 않았지만 새로운 AI 활용 방식을 적용시켜봤고, 팀원들과도 효과적으로 소통하고 협업했던 것 같아서 의미있던 시간이었던 것 같다.
'크래프톤 JUNGLE' 카테고리의 다른 글
| [Week8] WIL - 네트워크에 발 담그기 (1) | 2026.04.23 |
|---|---|
| [Week7] WIL - 무한 코어타임 지옥 (2) | 2026.04.16 |
| [Week5] WIL - DP 알고리즘 부수기 (0) | 2026.04.02 |
| [Week4] WIL - DFS BFS 정복기 (1) | 2026.03.26 |
| [Week3] WIL - 레디스 부수기 (0) | 2026.03.19 |