[주간 회고] 7주차: 가상 메모리의 심장, Malloc Lab 구현과 OS의 이해

이번 7주차는 Malloc Lab 프로젝트를 통해 C언어로 직접 동적 메모리 할당기를 구현하며 시스템 프로그래밍의 깊은 곳을 탐험한 시간이었습니다. 단순히 코드를 짜는 것을 넘어, 메모리를 어떻게 하면 더 효율적으로 관리할 것인가에 대해 치열하게 고민했던 기록들을 정리합니다.

1. 힙(Heap)의 구조와 경계 처리

메모리 할당기를 구현하기 위해 가장 먼저 힙의 물리적 및 논리적 구조를 설계했습니다.

  • 정렬(Alignment)과 패딩: 데이터 접근 성능 향상을 위해 8바이트 혹은 16바이트 경계를 맞추는 패딩(Padding)의 중요성을 배웠습니다.
  • 프롤로그 및 에필로그: 힙의 시작과 끝을 알리는 파수꾼(Sentinel) 블록을 두어, 메모리 순회 시 발생할 수 있는 경계 오류를 방지했습니다.

2. 가용 리스트 관리 전략: Implicit vs Explicit

가용 블록(Free Block)을 어떻게 찾아낼 것인가에 따라 할당기의 성능이 극명하게 갈리는 것을 확인했습니다.

  • Implicit Free List (묵시적 가용 리스트): 구현은 단순하지만 힙이 커질수록 탐색 비용이 블록 수에 비례하여 증가하는 비확장적 자료구조임을 체감했습니다.
  • Explicit Free List (명시적 가용 리스트): 가용 블록 내에 next, prev 포인터를 두어 탐색 속도를 높였습니다.
    • LIFO (Last-In-First-Out): 삽입 속도가 매우 빠르지만 단편화에 취약할 수 있습니다.
    • Address-Ordered: 주소 순으로 정렬하여 병합(Coalescing) 효율을 극대화하고 단편화를 방지했습니다.

 

implicit first fit/ explicit first fit/ explicit best fit 비교

3. 더 나은 성능을 위한 고도화 기법

기본적인 할당기를 넘어 실제 시스템에서 사용되는 고도화된 전략들을 학습했습니다.

  • 분리 가용 리스트(Segregated Free List): 가용 블록을 크기 구간별로 여러 리스트로 나누어 관리하여 탐색 시간을 단축했습니다.
  • 버디 시스템(Buddy System): 블록 크기를 2의 거듭제곱 단위로 관리하여 빠른 병합과 분할을 가능하게 하는 구조를 이해했습니다.

4. 운영체제 핵심 개념 정리

메모리 관리의 배경이 되는 커널의 동작 원리도 함께 정리했습니다.

  • System Call: sbrk나 mmap을 통해 사용자 프로세스가 커널에 메모리 자원을 요청하는 인터페이스를 학습했습니다.
  • 가상 메모리(Virtual Memory): 각 프로세스가 독립적인 메모리 공간을 가진 것처럼 추상화해주는 원리와 보안 및 안정성을 위한 격리의 중요성을 배웠습니다.

 

'Jungle > WIL(Weekly I Learned)' 카테고리의 다른 글

[WIL] 6주  (0) 2026.04.06
[WIL] 5주  (0) 2026.03.27
[WIL] 4주  (0) 2026.03.19
[WIL] 3주  (0) 2026.03.19
[WIL] 2주  (0) 2026.03.12

이동석코치님과 커피챗을 진행했다. 

같은 동기 한명과 진행했고, 요약해보자면

 

1. c언어는 portability가 약하고, 요즘은 rust를 많이 사용한다고한다. 다만 미국 이야기이고 한국은 아직이라고한다.

2. c언어는 매크로를 많이 사용하여 오픈소스 읽기가 힘들고, 여러가지 규칙이 있어서 이것을 먼저 아는 것이 이해하는데 도움이 된다고한다.

3. c언어의 성능이 느린부분을 어셈블리어로 교체하는 작업을 하는데, 함수마다 성능을 측정하는 것을 프로파일링이라고한다.

    프로파일링툴이 많으며 이것으로 성능을 측정하여 느리부분을 어셈블리어로 교체하여 성능을 개선한다.

4. 좋은 코드란 확장성과 다른사람이 읽기 쉬운 코드이다

 

 

 

'Jungle > Everyday' 카테고리의 다른 글

4/1 수요코딩 리액트(2)  (0) 2026.04.05
3/30 파이썬 비동기 프로그래밍  (0) 2026.03.31
3/27 DP(Dynamic Programming)  (0) 2026.03.28
3/21  (0) 2026.03.22
3/18  (0) 2026.03.19
카테고리 주간 실행 목표 (Action Item) 결과
01. 문제해결 매일 자료구조 c언어 구현 문제풀이로 논리적 사고 훈련
02. 설계 수요코딩시 아키텍쳐를 먼저 생각해보기
03. 구현 자료구조 구현시 엣지 케이스 생각하며 구현하기
04. 품질 자료구조 검증 테스트 케이스 추가 해보기
05. 유지보수 변수 명명에 의미를 담아 가독성 높은 코드 작성하기
06. 협업 작업 현황을 팀원들에게 즉시 공유하여 투명한 소통 유지
07. 태도 과제 부여 시 의도와 상위 카테고리를 먼저 분석하는 습관
08. 비즈니스 이해 사용성을 생각하며 코드 생산
09. AI & 생산성 하네스 사용해서 바이브 코딩 진행해보기
10. 학습 민첩성 SQL이 뭔지 이해하고 SQL 문장을 읽고, 파싱하고, 실행하는 처리기를 만들기

 

📅 이번 주 학습 및 프로젝트 요약 (4월 2주차)

이번 주 블로그에 기록했던 주요 프로젝트와 학습 내용을 한눈에 볼 수 있도록 정리했습니다.

1. C로 구현한 파일 기반 SQL 처리기

직접 SQL 파서와 실행기를 구현해 보는 6주차 프로젝트를 진행했습니다. Lexer와 Parser를 통해 SQL을 분석하고, 이를 바탕으로 실제 CSV 파일에 데이터를 INSERT하거나 SELECT 하는 흐름을 완성했습니다. AI를 전략적으로 활용해 빠르게 기능을 구현하면서도 핵심 로직을 스스로 이해하는 데 집중했습니다.

프로젝트 상세 보기 →

2. Pintos를 위한 C언어 핵심 정리

Pintos 과제 수행 전 필수적인 C언어의 메모리 관리와 포인터 개념을 정리했습니다. 파이썬과 대비되는 C언어의 특징, 주소와 값의 차이, malloc/free 활용법, 그리고 구조체 패딩(Padding) 최적화 등 실무적인 팁을 다루었습니다.

C언어 정리 확인하기 →

3. AI 시대, '고민의 시간'이 주는 가치

AI가 정답을 알려주는 시대에 왜 직접 고민하는 과정이 필요한지 정리한 에세이입니다. 문제 해결 근육을 키우는 '학습의 구덩이' 개념과 더불어, AI를 단순 정답기가 아닌 지적 가속기로 활용하는 전략(Time-boxing 등)을 공유했습니다.

에세이 읽어보기 →

4. 이동석 코치님과의 커피챗 (4/6)

현직 코치님과의 대화를 통해 Rust의 동향, C언어 오픈소스 분석 팁, 그리고 성능 최적화를 위한 프로파일링(Profiling)의 중요성을 배웠습니다. '좋은 코드란 무엇인가'에 대한 근본적인 고민을 할 수 있었던 유익한 시간이었습니다.

커피챗 요약 보기 →

#정글 #Jungle #SQL처리기 #C언어 #AI학습 #커피챗 #개발자성장기

'Jungle > WIL(Weekly I Learned)' 카테고리의 다른 글

[WIL] 7주  (0) 2026.04.16
[WIL] 5주  (0) 2026.03.27
[WIL] 4주  (0) 2026.03.19
[WIL] 3주  (0) 2026.03.19
[WIL] 2주  (0) 2026.03.12

 

Week 5 미니 React.ver2 구현 정리

이번 과제는 기존 React의 핵심 개념인 Component, State, Hooks를 직접 구현하고, 이를 바탕으로 실제로 동작하는 웹 페이지를 만드는 것이 목표였다. 우리 프로젝트는 단순히 React 문법을 흉내 내는 것이 아니라, FunctionComponent 클래스 + hooks 배열 + Virtual DOM diff/patch를 직접 구현한 뒤 Tic-Tac-Toe 데모 페이지를 그 위에서 동작하게 만든 구조다.

한 줄 요약
루트 컴포넌트 하나가 모든 상태를 관리하고, 자식 컴포넌트는 props만 받아 렌더링하며, 상태 변경 시 새로운 Virtual DOM을 만든 뒤 이전 트리와 비교해서 바뀐 부분만 실제 DOM에 반영하도록 구현했다.

1. 요구사항 정리

  • Component는 반드시 함수형 컴포넌트로 구현
  • FunctionComponent 클래스를 직접 만들어 hooks 배열, mount(), update()를 구현
  • Hook은 최상위 컴포넌트에서만 사용 가능
  • State는 루트 컴포넌트에서만 관리
  • 자식 컴포넌트는 props만 사용하는 Stateless Component로 구현
  • useState, useEffect, useMemo 직접 구현
  • Virtual DOM 생성 후 이전 트리와 비교하고, 변경된 부분만 Patch
  • 사용자 입력에 따라 화면이 바뀌는 테스트 페이지 제작
  • 외부 프레임워크 없이 JavaScript, HTML, CSS만 사용

2. 프로젝트 전체 구조

src/
  lib/
    runtime.js      // FunctionComponent, hooks, update scheduling
    vdom.js         // Virtual DOM 생성, diff, patch
  tic-tac-toe/
    model.js        // 게임 상태와 순수 로직
  demo/
    main.js         // 데모 페이지 엔트리
    styles.css      // 데모 페이지 스타일
tests/
  runtime.test.js
  vdom.test.js
  tic-tac-toe-model.test.js

3. Component 구현 방식

이 프로젝트의 핵심은 FunctionComponent 클래스다. 일반적인 React에서는 함수형 컴포넌트를 React 내부가 관리하지만, 이번 프로젝트에서는 우리가 직접 이 실행 환경을 만들었다.

3-1. FunctionComponent의 역할

  • hooks[] 배열로 state, memo, effect 정보를 저장
  • mount()로 첫 렌더 수행
  • update()로 상태 변경 후 재렌더 수행
  • 이전 Virtual DOM 트리와 현재 트리를 비교하여 patch 적용
  • 렌더 후 useEffect 실행
class FunctionComponent {
  constructor(renderFn, props = {}) {
    this.renderFn = renderFn;
    this.props = props;
    this.hooks = [];
    this.hookIndex = 0;
    this.container = null;
    this.currentTree = createRootVNode([]);
    this.pendingEffects = [];
    this.updateScheduled = false;
    this.isMounted = false;
  }

  mount(container) {
    this.container = container;
    this.renderAndCommit();
  }

  update(nextProps = this.props) {
    this.props = nextProps;
    this.renderAndCommit();
  }
}

3-2. 자식 컴포넌트는 왜 Stateless인가?

과제 조건에 따라 Hook과 State는 루트 컴포넌트에서만 사용할 수 있게 설계했다. 그래서 App만 상태를 가지고, Board, Square, MoveHistoryPanel 같은 자식 컴포넌트는 오직 props만 받아 화면을 그린다.

function App() {
  const [game, setGame] = useState(createInitialGameState);

  return h(Board, {
    board: getCurrentBoard(game),
    onSquareClick: (index) => setGame((currentGame) => playMove(currentGame, index)),
  });
}

function Board({ board, onSquareClick }) {
  return h(
    'section',
    {},
    ...board.map((value, index) =>
      h(Square, {
        value,
        onClick: () => onSquareClick(index),
      }),
    ),
  );
}

4. State 구현 방식

상태는 함수 내부 변수에 저장되지 않고, FunctionComponent 인스턴스의 hooks[] 배열에 저장된다. 렌더가 다시 일어나도 이 배열은 유지되기 때문에 상태도 유지된다.

4-1. 왜 상태가 유지되는가?

렌더 시작 시마다 hookIndex를 0으로 초기화하고, Hook을 호출한 순서대로 같은 슬롯을 재사용한다. 그래서 첫 번째 useState는 항상 같은 위치를 쓴다.

function getHook(component, name, expectedKind, createHook) {
  const index = component.hookIndex++;
  let hook = component.hooks[index];

  if (!hook) {
    hook = createHook();
    component.hooks[index] = hook;
  }

  return hook;
}

5. Hooks 구현 방식

5-1. useState

useState는 상태 값을 담는 slot을 만들고, 현재 값과 setter를 반환한다. setter는 즉시 렌더하지 않고 queue에 상태 변경 요청을 저장한다.

export function useState(initialValue) {
  const component = assertHookAccess('useState');
  const hook = getHook(component, 'useState', 'state', () => ({
    kind: 'state',
    queue: [],
    value: resolveInitialValue(initialValue),
  }));

  flushStateQueue(hook);

  const setState = (nextValue) => {
    hook.queue.push(nextValue);
    component.scheduleUpdate();
  };

  return [hook.value, setState];
}

5-2. setState는 무엇을 하는가?

  • 다음 상태를 queue에 저장
  • 루트 update를 예약
  • 다음 렌더에서 queue를 반영
  • 새 Virtual DOM 생성
  • diff 후 patch

5-3. batching

같은 이벤트 안에서 여러 번 호출된 setState는 하나의 microtask로 묶어 처리한다. 즉 여러 상태 변경을 한 번의 재렌더로 합친다.

scheduleUpdate() {
  if (this.updateScheduled || !this.container) {
    return;
  }

  this.updateScheduled = true;

  queueMicrotask(() => {
    this.updateScheduled = false;
    this.update(this.props);
  });
}

5-4. useMemo

useMemo는 dependency가 바뀌었을 때만 다시 계산된다. 이 프로젝트에서는 승패 계산, 말 개수 계산, 상태 문구 생성에 사용했다.

const result = useMemo(() => calculateResult(board), [board]);
const moveCount = useMemo(() => getMoveCount(board), [board]);

5-5. useEffect

useEffect는 렌더 중 실행되지 않고 commit 후 실행된다. 또한 다시 실행되기 전 이전 cleanup을 먼저 실행하도록 만들었다.

export function useEffect(callback, deps) {
  const component = assertHookAccess('useEffect');
  const hook = getHook(component, 'useEffect', 'effect', () => ({
    kind: 'effect',
    cleanup: null,
    deps: undefined,
  }));

  if (shouldRunHook(hook.deps, deps)) {
    component.pendingEffects.push({ callback });
    hook.deps = [...deps];
  }
}

6. Virtual DOM + Diff + Patch 구현

6-1. Virtual DOM 생성

이 프로젝트는 JSX 대신 h() 함수로 Virtual DOM 노드를 생성한다. 실제 DOM을 바로 만드는 것이 아니라, 먼저 UI 구조를 JavaScript 객체로 만든다.

h('button', { onClick: handleClick }, 'Click')

6-2. 초기 렌더

첫 렌더에서는 이전 트리가 없으므로 diff 없이 전체 Virtual DOM을 실제 DOM으로 마운트한다.

if (!this.isMounted) {
  mountVNode(this.container, nextTree);
  this.isMounted = true;
}

6-3. update 시 diff / patch

상태가 바뀌면 새 트리를 만들고 이전 트리와 비교해서, 변경된 부분만 실제 DOM에 반영한다.

  • UPDATE_PROPS
  • UPDATE_TEXT
  • INSERT_CHILD
  • REMOVE_CHILD
  • MOVE_CHILD
const operations = patchDom(this.container, this.currentTree, nextTree);

7. 테스트 페이지: Tic-Tac-Toe

데모 페이지는 사용자 입력에 따라 상태가 확실히 바뀌고, Hook과 Virtual DOM 동작을 설명하기 좋은 Tic-Tac-Toe 게임으로 만들었다.

7-1. 상태 변화 흐름

  1. 사용자가 칸 클릭
  2. Square가 props로 받은 onClick 실행
  3. 루트 AppsetGame() 호출
  4. 새 상태가 queue에 저장
  5. microtask에서 update 실행
  6. 새 Virtual DOM 생성
  7. diff
  8. patch
  9. effect 실행

7-2. 자식 컴포넌트에서 루트로 데이터 전달

자식 컴포넌트는 상태를 직접 수정하지 않고, 루트가 내려준 콜백을 호출해서 클릭 정보만 전달한다.

function Square({ index, onClick, value }) {
  return h(
    'button',
    {
      onClick,
      type: 'button',
    },
    value || '',
  );
}
function Board({ board, onSquareClick }) {
  return h(
    'section',
    {},
    ...board.map((value, index) =>
      h(Square, {
        value,
        onClick: () => onSquareClick(index),
      }),
    ),
  );
}

즉, 클릭된 칸 번호 같은 데이터는 이벤트 인자로 부모에게 전달되고, 실제 state 변경은 루트에서만 일어난다.

8. Component Lifecycle을 이 프로젝트 기준으로 설명하면

단계 설명
생성 new FunctionComponent(App) 시 hooks 배열과 상태 플래그 초기화
Mount 첫 render 후 Virtual DOM 생성, 실제 DOM 마운트
Update setState 이후 새 트리 생성, diff, patch
Commit 실제 DOM 반영 완료
Effect commit 후 useEffect 실행
Cleanup 다음 effect 실행 전 이전 cleanup 수행

9. 테스트 코드로 검증한 내용

  • 루트 state 저장 및 DOM 업데이트
  • 동일 이벤트 내 여러 setState 호출 시 batching
  • useMemo 재계산 조건
  • useEffect 실행 및 cleanup 순서
  • 자식 컴포넌트의 Hook 사용 금지
  • Virtual DOM diff/patch 정상 동작
  • key 기반 자식 이동 처리
  • Tic-Tac-Toe 상태 전이와 승패 계산

10. 실제 React와의 차이점

  • Hook을 루트 컴포넌트에서만 허용
  • Fiber 구조 없음
  • Concurrent Rendering 없음
  • 우선순위 기반 스케줄링 없음
  • Effect 처리 방식이 단순화됨
  • Reconciliation이 최소 기능만 구현됨

11. 마무리

이번 과제를 통해 React를 단순히 사용하는 수준이 아니라, 상태가 어떻게 유지되고, 렌더가 어떻게 다시 일어나며, DOM이 왜 전체가 아니라 필요한 부분만 갱신되는지를 직접 구현하며 이해할 수 있었다.

  • 함수는 다시 실행되어도 상태는 hooks 배열에 남아 있어야 한다.
  • setState는 값 변경이 아니라 재렌더 흐름의 시작점이다.
  • Virtual DOM diff/patch는 DOM 업데이트를 효율적으로 만든다.

결과적으로 이번 프로젝트는 과제 요구사항을 충실히 반영하면서도, 미니 React 런타임의 핵심 개념을 설명할 수 있는 구조로 완성되었다.

'Jungle > Everyday' 카테고리의 다른 글

4/6 커피챗  (0) 2026.04.06
3/30 파이썬 비동기 프로그래밍  (0) 2026.03.31
3/27 DP(Dynamic Programming)  (0) 2026.03.28
3/21  (0) 2026.03.22
3/18  (0) 2026.03.19

⚙️ 1. 비동기 함수 개념 정리

분류 함수/키워드 의미 (비유) 주요 역할
정의 async def 예약 가능한 함수 호출 즉시 실행되지 않고, 나중에 실행할 코루틴 객체를 생성합니다.
제어 await 기다리는 동안 양보 작업 완료를 기다리는 동안 이벤트 루프에 제어권을 넘겨 다른 작업이 실행되게 합니다.
실행 asyncio.run() 엔진 시동 이벤트 루프를 생성하고 메인 코루틴을 실행한 뒤 안전하게 종료합니다.
조율 asyncio.gather() 동시 실행 여러 비동기 작업을 한꺼번에 시작하고, 모두 끝날 때까지 기다려 결과를 모읍니다.
조율 asyncio.as_completed() 빠른 순 처리 완료된 작업부터 순서대로 결과를 받을 수 있게 합니다.
관리 get_running_loop() 현재 루프 조회 현재 실행 중인 이벤트 루프 객체를 가져옵니다.
도구 httpx.AsyncClient() 비동기 브라우저 비동기 방식으로 HTTP 요청을 보낼 수 있게 해주는 클라이언트입니다.

💻 2. 실습 코드

import asyncio
import time
import requests
import httpx

URLS = [
    "https://www.google.com",
    "https://www.python.org",
    "https://www.github.com",
    "https://www.naver.com",
    "https://www.wikipedia.org"
] * 2


# --- [방법 1] 동기 방식: 하나씩 순서대로 실행 ---
def fetch_sync():
    print("동기(Sync) 방식 시작...")
    start_time = time.perf_counter()
    results = []

    for url in URLS:
        response = requests.get(url)
        results.append(response.status_code)
        print(f"[Sync] {url} 완료")

    end_time = time.perf_counter()
    return end_time - start_time


# --- [방법 2] 비동기 방식: 대기 시간에 다른 작업 수행 ---

# 2-1. 개별 URL을 처리하는 코루틴
async def fetch_async_url(client, url):
    response = await client.get(url)
    print(f"[Async] {url} 완료")
    return response.status_code


# 2-2. 여러 비동기 작업을 조율하는 메인 코루틴
async def fetch_async():
    print("\\n비동기(Async) 방식 시작...")
    start_time = time.perf_counter()

    async with httpx.AsyncClient() as client:
        tasks = [fetch_async_url(client, url) for url in URLS]
        await asyncio.gather(*tasks)

    end_time = time.perf_counter()
    return end_time - start_time


# --- 프로그램 실행 시작점 ---
if __name__ == "__main__":
    sync_duration = fetch_sync()
    async_duration = asyncio.run(fetch_async())

    print("\\n" + "=" * 30)
    print(f"동기 방식 소요 시간  : {sync_duration:.2f}초")
    print(f"비동기 방식 소요 시간: {async_duration:.2f}초")
    print(f"비동기가 {sync_duration / async_duration:.1f}배 더 빠릅니다!")
    print("=" * 30)

🧠 3. 비동기 vs 멀티스레딩

🔥 핵심 개념

  • 비동기 = 한 명의 작업자가 여러 작업을 번갈아 처리하는 방식
  • 멀티스레딩 = 여러 작업자가 동시에 작업을 처리하는 방식

📊 동시성 vs 병렬성

개념 설명
동시성 (Concurrency) 하나의 스레드가 여러 작업을 번갈아 수행하여 동시에 진행되는 것처럼 보이게 하는 방식
병렬성 (Parallelism) 여러 스레드나 여러 코어가 실제로 동시에 작업을 실행하는 방식

 

⚖️ Async vs Thread 비교

구분 비동기 (Asyncio) 멀티스레딩 (Threading)
스레드 개수 1개 (Single Thread) 여러 개 (Multiple Threads)
관리 주체 애플리케이션 / 이벤트 루프 운영체제
리소스 비용 낮음 높음
작업 전환 방식 await 지점에서 협력적으로 전환 운영체제 스케줄러가 강제로 전환
주요 강점 I/O 대기 시간을 효율적으로 활용 실제 병렬 처리 가능

❓ 왜 Python은 Async를 많이 사용할까?

파이썬에는 GIL(Global Interpreter Lock)이라는 제약이 있습니다.
이 때문에 멀티스레드를 사용하더라도 CPU 바운드 작업에서는 기대만큼 성능 향상이 크지 않을 수 있습니다.

하지만 웹 요청, 데이터베이스 쿼리, 파일 읽기/쓰기 같은 I/O 작업은 대부분의 시간이 "응답을 기다리는 시간"입니다.
비동기는 바로 이 대기 시간을 다른 작업으로 채우기 때문에, 적은 리소스로도 많은 작업을 효율적으로 처리할 수 있습니다.

🧾 4. 핵심 요약

  • async는 여러 일을 동시에 “보이게” 처리하는 동시성 모델이다.
  • await는 기다리는 동안 다른 작업에게 순서를 넘기는 핵심 지점이다.
  • 파이썬 비동기는 특히 네트워크, API 호출, DB 통신 같은 I/O 작업에서 강력하다.
  • CPU 연산이 많은 작업은 비동기보다 멀티프로세싱이 더 적합한 경우가 많다.

📚 5. 참고 자료

'Jungle > Everyday' 카테고리의 다른 글

4/6 커피챗  (0) 2026.04.06
4/1 수요코딩 리액트(2)  (0) 2026.04.05
3/27 DP(Dynamic Programming)  (0) 2026.03.28
3/21  (0) 2026.03.22
3/18  (0) 2026.03.19

큰 문제를 작은 부분 문제로 나누어 해결

부분 문제의 결과를 저장하여 재사용

중복 계산을 제거하여 효율성 향상

 

피보나치 수열 [재귀 O(2^n)] -> [DP O(n)] 으로 극적인 성능 향상!

 

메모이제이션

- 계산 결과를 memo에 저장

- 같은 값을 다시 계산할 필요 없음

- 캐싱과 유사한 개념

 

DP가 필요한 경우

1. 최적 부분 구조: 부분 문제의 최적해로 전체 최적해 구성

2. 중복 부분 문제: 같은 문제가 반복적으로 등장

 

 

https://leetcode.com/problems/house-robber/description/?envType=study-plan-v2&envId=top-interview-150

 

House Robber - LeetCode

Can you solve this real interview question? House Robber - You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent ho

leetcode.com

 

연속 해서 집을 털 수 없다.

dp 배열을 어떻게 정의 해야 할지가 중요하다

dp[n]= n번째까지 집을 털었을 경우 최대 금액

nums 에 각 집의 소지액을 나타내는 정수 배열이 주어졌다.

 

max( n번째 집을 선택했을 경우, n번째 집을 선택하지 않았을 경우 )

n번째 집을 선택했을 경우) 연속된 n-1번째는 선택하지 못한다. -> nums[n] + dp[n-2]

n번째 집을 선택하지 않았을 경우) n-1번째까지의 최댓값이 된다.

dp[n]= max(dp[n-2] + nums[n], dp[n-1])

'Jungle > Everyday' 카테고리의 다른 글

4/1 수요코딩 리액트(2)  (0) 2026.04.05
3/30 파이썬 비동기 프로그래밍  (0) 2026.03.31
3/21  (0) 2026.03.22
3/18  (0) 2026.03.19
3/17  (0) 2026.03.18
카테고리 주간 실행 목표 (Action Item) 결과
01. 문제해결 매일 알고리즘 문제 1개 풀이로 논리적 사고 훈련
02. 설계 수요코딩시 아키텍쳐를 먼저 생각해보기
03. 구현 dp,  greedy 알고리즘 문제 난이도 상까지 구현해보기
04. 품질 기본 테스트 케이스 외에 나만의 예외 케이스 1개 추가 검증
05. 유지보수 변수 명명에 의미를 담아 가독성 높은 코드 작성하기
06. 협업 작업 현황을 팀원들에게 즉시 공유하여 투명한 소통 유지
07. 태도 과제 부여 시 의도와 상위 카테고리를 먼저 분석하는 습관
08. 비즈니스 이해 사용성을 생각하며 코드 생산
09. AI & 생산성 Agent.md 파일 및 바이브코딩  시 입력하는 기획서를 다른 방법을 찾아 작성 해보기
10. 학습 민첩성 React Component, State, Hooks 학습 및 구현

 

5주차 후기

매일 알고리즘 문제 1문제 푸는 목표를 수요일 목요일 풀지 못했다.

파이썬 스터디에 들어가서 깊게 공부 할 수 있어 좋았고, 다양한 사람들과 공부 할 수 있어서 좋았다.

수요코딩에서 리액트 구현시 작업현황을 팀원들에게 바로 공유해서 투명한 소통을 유지했다.

또한 1시간 혹은 2시간 단위로 공부하고 회의해서 시간 제한을 통해 루즈해지지 않게 프로젝트를 진행해서 서로 공부한 것을 빨리 공유 할 수 있었다.

 

"수요코딩시 코드 이해할때 내가 모르는 언어라도 모든 코드에 주석을 달아서 이해 할 수 있다" 라는 말을 

다른 팀원에게서 들었다. 또한 비전공자도 이해 할 수 있게 프로젝트 코드를 바탕으로 AI를 사용해 학습자료를 만들었다는 말에

정말 머리가 띵했다.

나는 이 정도로 코드를 이해하려고 했을까?? 

이제 3번정도 남았는데 내가 바이브코딩을 하면 주석을 모든 줄에 달고 사용한 프로그래밍 언어 문법을 바탕으로 자료를 만들어서 팀원들의 이해를 돕고 싶다!!!! 

 

 

3/27 DP

https://forrest7.tistory.com/41

 

3/27 DP(Dynamic Programming)

큰 문제를 작은 부분 문제로 나누어 해결부분 문제의 결과를 저장하여 재사용중복 계산을 제거하여 효율성 향상 피보나치 수열 [재귀 O(2^n)] -> [DP O(n)] 으로 극적인 성능 향상! 메모이제이션- 계

forrest7.tistory.com

 

3/30 비동기 프로그래밍

https://forrest7.tistory.com/42

 

3/30 파이썬 비동기 프로그래밍

⚙️ 1. 비동기 함수 개념 정리분류함수/키워드의미 (비유)주요 역할정의async def예약 가능한 함수호출 즉시 실행되지 않고, 나중에 실행할 코루틴 객체를 생성합니다.제어await기다리는 동안 양보

forrest7.tistory.com

 

4/1 수요코딩 리액트2

https://forrest7.tistory.com/43

'Jungle > WIL(Weekly I Learned)' 카테고리의 다른 글

[WIL] 7주  (0) 2026.04.16
[WIL] 6주  (0) 2026.04.06
[WIL] 4주  (0) 2026.03.19
[WIL] 3주  (0) 2026.03.19
[WIL] 2주  (0) 2026.03.12

boj 11725 트리의 부모 찾기

https://www.acmicpc.net/problem/11725

 

그래프를 인접리스트로 저장한다.

bfs를 진행하면서 parent배열(정점의 부모를 나타냄) 이 없고 a->b로 가는 간선이 있으면

parent[b]=a를 진행한다.

 

bfs를 부모노드를 찾는 곳에 쓰이리라고는 생각도 못했다. 

 

leetcode 섬의 개수

https://leetcode.com/problems/number-of-islands/description/?envType=study-plan-v2&envId=top-interview-150

 

Number of Islands - LeetCode

Can you solve this real interview question? Number of Islands - Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands. An island is surrounded by water and is formed by connecting adjacent l

leetcode.com

bfs, dfs로 육지로 이루어진(1로 이루어짐) 섬의 개수를 세야한다.

 

bfs, dfs로  Flood Fill( 다차원 배열에서 연결된 영역을 찾는 알고리즘) 이 가능하다.

'Jungle > Everyday' 카테고리의 다른 글

3/30 파이썬 비동기 프로그래밍  (0) 2026.03.31
3/27 DP(Dynamic Programming)  (0) 2026.03.28
3/18  (0) 2026.03.19
3/17  (0) 2026.03.18
3/16  (0) 2026.03.17

4주차 목표 설정

카테고리 주간 실행 목표 (Action Item) 결과
01. 문제해결 매일 알고리즘 문제 1개 풀이로 논리적 사고 훈련 ⭐ ⭐ ⭐
02. 설계 코드 작성 전, 주석으로 로직을 먼저 설계하는 습관 갖기
03. 구현 React 공식 문서 실습을 통해 탄탄한 기본기 구현
04. 품질 기본 테스트 케이스 외에 나만의 예외 케이스 1개 추가 검증
05. 유지보수 변수 명명에 의미를 담아 가독성 높은 코드 작성하기
06. 협업 작업 현황을 팀원들에게 즉시 공유하여 투명한 소통 유지 ⭐ ⭐
07. 태도 과제 부여 시 의도와 상위 카테고리를 먼저 분석하는 습관
08. 비즈니스 이해 사용자 입장에서 UI/UX의 편의성을 고민하며 코드 생산
09. AI & 생산성 배포 자동화 프로세스를 구축하여 개발 생산성 극대화
10. 학습 민첩성 기존의 단순 Merge 대신 Pull Request를 활용한 코드 리뷰 및 흐름 파악 ⭐ ⭐ ⭐

 

 

4주차 목표 후기

4주차에 설정한 목표를 대부분 해내지 못했다.

특히 협업시 바이브코딩으로 만들어낸 소스코드를 내가 이해하지 못했다.

남의 코드 역시 100% 이해하지 못했고 이 부분을 내가 알아 볼 수 있고 설명 할 수 있는 레벨에서 코드를 생성해야겠다고 느꼈다.

3/21 BFS DFS

트리의 부모 찾기, leetcode 섬의 개수

https://forrest7.tistory.com/39

 

[React] "React는 왜 빠를까?" 딥다이브: Virtual DOM과 Diffing 알고리즘

 

1. 왜 DOM이 있는데 Virtual DOM을 만들까?

브라우저의 실제 DOM은 느리지 않습니다. 진짜 느린 것은 DOM이 변경될 때 발생하는 브라우저의 '렌더링 과정(Reflow/Repaint)'입니다.

브라우저의 가혹한 렌더링 과정

JavaScript로 DOM을 조작할 때마다 브라우저는 다음 과정을 거칩니다.

  1. Recalculate Style: 스타일 계산
  2. Layout (Reflow): 요소의 크기와 위치 재계산
  3. Paint: 화면에 그리기

만약 1,000개의 리스트를 하나씩 수정하면 이 무거운 과정을 1,000번 반복할 수도 있습니다. Virtual DOM은 일종의 '버퍼링' 역할을 하여, 변경 사항을 모았다가 실제 DOM에 단 한 번만 반영(Batch Update)함으로써 이 비용을 획기적으로 줄입니다.

2. VDOM은 항상 빠를까? (반전 주의)

"전체 HTML 페이지를 다 수정할 때는 오히려 VDOM이 더 느립니다."
그 이유는 간단합니다.

  • 새로운 VDOM 트리를 생성해야 함
  • 이전 트리와 비교(Diffing) 알고리즘을 돌려야 함
  • 결국 실제 DOM도 전체를 다 갈아엎어야 함

즉, 작업 단계가 하나 더 추가되는 셈입니다. VDOM은 데이터가 부분적으로 자주 바뀔 때 최적의 효율을 냅니다.

3. React Diffing 알고리즘: O(n)의 비밀

React가 사용하는 Diffing 알고리즘이 O(n^3)에서 O(n)으로 파격적인 다이어트를 할 수 있었던 이유는 "완벽한 최소 변경 경로"를 찾는 것을 포기하고, "현실적인 지름길"을 선택했기 때문입니다. 이를 Heuristic Reconciliation(휴리스틱 비교)이라고 부릅니다.

 

3-1. 계층별 비교 (Level-by-Level Check)

  • 동일 레벨 비교: React는 트리를 순회할 때 같은 층위에 있는 노드들끼리만 비교합니다.
  • 비교 생략: 만약 어떤 노드가 부모를 갈아탔다면, React는 이를 '이동'으로 인식하지 않고 이전 부모 아래의 노드는 삭제, 새 부모 아래의 노드는 새로 생성합니다.
  • 이 덕분에 전체 트리를 훑지 않고 위에서 아래로 딱 한 번만 훑고 내려가면 끝납니다. (O(n)의 첫 번째 비결)

3-2. 엘리먼트 타입에 의한 가지치기 (Bailing out by Type)

  • 타입이 다르면? (<div> rightarrow <span>): 그 아래에 자식이 1,000개가 달려 있어도 React는 더 이상 비교하지 않습니다. "아, 이건 아예 다른 거구나"라고 결론짓고 기존 서브트리를 통째로 날린 뒤 새로 만듭니다.
  • 타입이 같으면? (<div> rightarrow <div>): 오직 변경된 속성(Attribute)이나 텍스트 내용만 업데이트하고 다음 자식으로 넘어갑니다.
  • 이 방식은 복잡한 하위 트리 비교 연산을 순식간에 종료(Short-circuit)시켜 버립니다.

3-3. Key를 이용한 Map 매핑 (The Key Optimization)

  • 기존 방식: 첫 번째 요소부터 하나씩 대조하며 "얘가 걔인가?" 확인.
  • Key 방식: 새로운 리스트를 만들 때 기존 Key들을 Map에 담아둡니다. ({ 'key_a': Node_A, 'key_b': Node_B })
  • 새로운 노드를 렌더링할 때 Map에서 해당 Key를 바로 찾아내어(Map.get(key)) 위치만 옮깁니다.
  • 결과: 리스트를 한 번 순회($n$)하면서 Map에서 값을 찾는 과정(상수 시간)만 거치므로 리스트 비교 역시 O(n)에 수렴하게 됩니다.
  • 가장 결정적인 부분입니다. 자식 리스트를 비교할 때 Key가 없다면 순서대로 비교해야 하므로 O(n^2)에 가까운 연산(비효율적인 업데이트)이 발생할 수 있지만, Key가 있으면 이를 Hash Map 구조처럼 사용합니다.
  • React는 노드의 타입(HTML 태그나 컴포넌트 이름)을 가장 먼저 확인합니다.
  • 일반적인 트리 비교 알고리즘은 한 트리의 노드가 다른 트리의 어느 위치로든 갈 수 있다고 가정하고 모든 조합을 계산합니다. 하지만 React는 "웹 페이지에서 노드가 다른 부모 밑으로 가는 경우는 드물다"고 판단했습니다.
  • 왜 O(n)이 가능한지 그 내부 로직을 3가지 핵심 포인트로 짚어드릴게요.

4. 리스트와 Key: 핵심 피드백 정리

Q. Key가 없으면 어떻게 동작할까?

React는 자식들을 순서대로 비교합니다. 리스트 맨 앞에 새 요소가 추가되면, React는 "모든 요소가 바뀌었다"고 착각하고 전체를 다시 그리는 대참사가 일어납니다.

Q. UUID를 굳이 써야 할까?

아니요, 그럴 필요 없습니다. Key는 형제(Sibling) 사이에서만 고유하면 됩니다. 데이터베이스의 ID값이 있다면 그것이 베스트입니다. 전역적으로 유니크할 필요는 없습니다.

Q. Key를 넣는 게 무조건 나을까?

네, 리스트 렌더링 시 Key는 필수입니다. Key가 없으면 React는 인덱스 번호로 비교를 시도하며, 이는 데이터 정렬이나 삭제 시 예기치 못한 버그와 성능 저하를 일으킵니다.

Q. Key가 중복되면? (React vs Vue)

  • React: 콘솔 경고를 띄우고, 첫 번째 요소만 정상 처리하거나 렌더링이 꼬일 수 있습니다.
  • Vue: 비슷하게 경고를 주지만, 내부 최적화 방식에 따라 렌더링 결과가 다를 수 있습니다. 결론은 둘 다 중복 Key는 금기 사항입니다.

5. 심화 질문과 실무 가이드

비교할 때 '해시(Hash)'를 쓰면 더 빠르지 않을까?

이론적으로는 가능하지만, 트리 전체의 해시를 만드는 비용 자체가 큽니다. React는 이미 충분히 빠른 타입 비교와 Key 식별 전략을 택해 실용적인 최적화를 이루었습니다.

렌더링 최적화 (텍스트)

React는 div 안의 텍스트가 바뀔 때, 전체 텍스트를 지우고 새로 쓰는 게 아니라 변경된 마지막 부분만 취하는 방식 등으로 내부적인 최적화를 수행합니다.

⚠️ 실무에서 조심해야 할 점

  • data-key 사용: 리스트 엘리먼트에 Key를 명시하지 않으면 React가 훨씬 더 많은 연산을 수행해야 합니다.
  • State의 특성: React의 State는 메모리(RAM)에 저장됩니다. 브라우저를 새로고침하면 사라지는 이유가 바로 이것입니다. 히스토리를 관리하고 싶다면 별도의 저장 로직이 필요합니다.

6. 결론: 협업과 고도화

수요코딩이나 실무 협업 시, Requirement(명세서)를 고도화하면서 업무를 분할해 보세요.

"이 컴포넌트는 리스트 양이 많으니 고유 ID를 Key로 바인딩하고, 불필요한 재렌더링을 막기 위해 메모이제이션을 적용한다"

위와 같은 기준을 명세서에 녹여내면 더 수준 높은 개발이 가능해집니다!

'Jungle > WIL(Weekly I Learned)' 카테고리의 다른 글

[WIL] 7주  (0) 2026.04.16
[WIL] 6주  (0) 2026.04.06
[WIL] 5주  (0) 2026.03.27
[WIL] 3주  (0) 2026.03.19
[WIL] 2주  (0) 2026.03.12

🎯 목표 설정 및 수행 결과 회고

1. 문제 해결 (Algorithm)

  • 목표: 알고리즘 카테고리에 따라 매일 1문제 스스로 풀기
  • 결과: 매일 실천에 어려움이 있었으며, 블로그 참조와 AI 코드 리뷰에 의존함
  • Action Plan: 30분간은 외부 도움 없이 스스로 풀고, 이후 즉시 피드백(블로그, AI)을 받는 방식으로 전환

2. 설계 (Architecture)

  • 목표: 확장 가능한 폴더 구조 설계로 협업 시 Git 충돌 방지
  • 결과: Redis 구현 시 테스트 폴더 구조를 분리하여 바이브 코딩 중 코드 혼선 방지 성공

3. 구현 (Implementation)

  • 목표: 바이브 코딩 시 명세서를 작성하여 AI의 프로젝트 문맥 파악 돕기
  • 결과: 명세서 기반 학습은 이루어졌으나, Google Docs 사용으로 가독성 저하
  • Action Plan: 향후 Notion 활용 및 명세서 자동화 방안 모색

4. 품질 (Quality)

  • 목표: Redis 관련 프로젝트의 모든 테스트 통과
  • 결과: Github Actions로 자동화 구축. Main 브랜치 수정 시 테스트 자동 실행 및 결과를 Notion에 공유하는 시스템 마련

5. 유지보수 (Maintenance)

  • 목표: 주석 작성을 통한 팀원 간 이해도 증진
  • 결과: 기능 구현에 집중하느라 주석 및 유지보수 고려 부족 (미흡)

6. 협업 (Collaboration)

  • 목표: 능동적인 태도로 협업 속도 향상
  • 결과: 명세서 사전 작성을 통해 팀원들의 프로젝트 이해도 및 작업 속도 증진

7. 태도 (Mindset)

  • 목표: Redis의 코어 개념과 원리를 학습 후 구현
  • 결과: 캐시 개념은 공부했으나 실무 적용에 한계. '왜 우리에게 Redis가 필요한가'에 대한 논리적 근거 부족

8. 비즈니스 이해 (Business Insight)

  • 목표: 사용자 편의성(UX)을 고려한 코드 및 프로젝트 진행
  • 결과: 사용자 관점보다는 기술적 구현에만 매몰됨 (미흡)

9. AI 활용 (AI Prompting)

  • 목표: Agent.md 최적화를 통한 고품질 코드 생성
  • 결과: OpenAI 팁을 참고하여 Agent.md를 영문으로 작성하고 한국어 설명을 병기하여 효과적인 가이드 마련

10. 학습 민첩성 (Learning Agility)

  • 목표: Redis 내부 구조 이해 및 구현
  • 결과: 깊이 있는 이해 부족으로 Python 기본 딕셔너리를 무분별하게 사용
  • Action Plan: 내부 구현 사항을 선행 학습하고, 기술 선택 시 구체적인 이유를 제시할 수 있도록 준비

 

🔍 4주차 발제: 더 깊게 고민할 사항

1. 파이썬 자료구조의 내부 구현

  • List: 연결 리스트(Linked List)가 아닌 동적 배열(Dynamic Array)로 구현됨
  • Dictionary: Hash 방식 사용. Python 3.6+부터 메모리 효율을 위해 인덱스 테이블엔트리 테이블을 분리
    • 데이터 삽입 순서가 유지되도록 개선됨 (Open Addressing 기반)
  • Sort(): Timsort (Insertion + Merge Sort 혼합) 사용
    • 최선: O(n), 최악: O(n \log n)의 성능 보장

2. WebAssembly (Wasm)와 저수준 언어의 필요성

  • 현상: JS는 인터프리터 기반이라 복잡한 연산(영상 편집, 3D, 암호화)에서 병목 발생
  • 해결: C/Rust로 로직 설계 후 Wasm으로 컴파일하여 네이티브에 가까운 속도 구현
  • 전략: Python으로 빠르게 프로토타입 제작 후, 성능이 필요한 구간은 빠른 언어로 전환

3. Redis Hash 직접 구현 시 고려사항

  • 타 언어에서 직접 Hash를 구현해야 한다면 다음 세 가지가 핵심임:
    1. 해시 함수 선정
    2. 충돌(Collision) 처리 방식 결정
    3. 동적 리사이징: 데이터 임계치 초과 시 테이블 크기를 확장(보통 2배)하고 재배치(Rehashing)
    Note: Redis는 소량 데이터일 땐 ziplist(압축 구조), 많아지면 hashtable로 전환하는 유연성을 가짐

4. 바이브 코딩 결과물에 대한 '진짜' 내 것 만들기

  • AI가 만든 결과물에 대해 답변하지 못한다면 그것은 본인의 실력이 아님
  • 수요코딩회 활용: 내가 완벽히 소화할 수 있는 범위만큼만 AI를 활용하고, 그 내용을 공유하며 검증받기

 

📒 3주차 블로그 정리

Merge Sort, Quick Sort, 분할 정복

https://forrest7.tistory.com/32

 

3/13

Merge sort리스트 길이가 1이하이면 이미 정렬된 것으로 본다.1. 분할 : 정렬되지 않은 리스트를 절반으로 잘라 비슷한 크기의 두부분 리스트로 나눈다.2. 정복 : 각 부분 리스트를 재귀적으로 합병

forrest7.tistory.com

 

스택, set (boj1406 에디터, boj2295 세수의 합)

https://forrest7.tistory.com/33

 

3/14

에디터(boj 1406)https://www.acmicpc.net/problem/1406 스택을 두개 둔 다음 1) 커서가 왼쪽으로 이동할때 파란 스택에서 빼서 빨간 스택에 넣는다2) 커서가 오른쪽으로 이동할때 빨간 스택에서 빼서 파란 스

forrest7.tistory.com

Queue (boj 3190 뱀)

https://forrest7.tistory.com/34

 

3/16

boj 3190 뱀1. 출발점의 위치 [0,0] 에 방문 표시를 하지 않고, 큐에 넣지 않아서 디버깅 시간이 오래 걸렸다.2. dr,dc 배열을 따로두고 인덱스가 늘어나면 시계방향으로 회전되게 한다.# 인덱스 증가시

forrest7.tistory.com

투 포인터 (boj 2470 두 용액)

https://forrest7.tistory.com/35

 

수요 코딩 후기 (레디스 구현)

https://forrest7.tistory.com/36

'Jungle > WIL(Weekly I Learned)' 카테고리의 다른 글

[WIL] 7주  (0) 2026.04.16
[WIL] 6주  (0) 2026.04.06
[WIL] 5주  (0) 2026.03.27
[WIL] 4주  (0) 2026.03.19
[WIL] 2주  (0) 2026.03.12

+ Recent posts