<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>경이로운 개발일기</title>
    <link>https://wondrous-developer.tistory.com/</link>
    <description>경이로운 BE 개발자가 되기 위한 프로그래밍 공부 기록장</description>
    <language>ko</language>
    <pubDate>Wed, 19 Aug 2026 18:59:09 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>BE_개발자</managingEditor>
    <image>
      <title>경이로운 개발일기</title>
      <url>https://tistory1.daumcdn.net/tistory/6670458/attach/e90217ff556d4eb6a11753c4b44af4a7</url>
      <link>https://wondrous-developer.tistory.com</link>
    </image>
    <item>
      <title>[하네스 엔지니어링] 하네스 엔지니어링을 너무 일찍 적용해보고 깨달은 것</title>
      <link>https://wondrous-developer.tistory.com/275</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이 글은 AI 코딩 에이전트를 잘 쓰기 위해 하네스를 만들다가, 오히려 하네스가 프로젝트보다 커져버린 경험에 대한 회고입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구를 쓰다 보면 어느 순간 이런 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;이거 매번 설명하기 귀찮은데, 규칙으로 박아두면 되지 않을까?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;에이전트 역할을 나눠두면 더 체계적으로 돌아가지 않을까?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;이슈, 브랜치, 리뷰, 커밋 규칙까지 자동화하면 좋지 않을까?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 최근 컴퓨터 비전 프로젝트를 기획하면서 비슷한 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 주제는 최신 SAM 계열 모델을 단순한 2D segmentation 도구로 쓰는 것이 아니라, 3D 공간에서 객체를 추적하고, 측정하고, 배치 가능성까지 판단하는 object prior 생성기로 활용하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적으로 꽤 복잡한 프로젝트였기 때문에 처음부터 Claude와 Codex가 프로젝트를 잘 이해할 수 있는 구조를 만들고 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다음과 같은 것들을 한 번에 구성했다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;CLAUDE.md
.claude/PROJECT.md
.claude/agents/
.claude/skills/
.claude/rules/
.claude/tasks/
.github/ISSUE_TEMPLATE/
.github/PULL_REQUEST_TEMPLATE.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로 보기에는 꽤 그럴듯했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude는 진입점을 읽고, 라우터를 통해 역할을 정하고, 이슈를 만들고, 작업을 나누고, PR까지 만들 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 막상 시작하고 보니 이상한 피로감이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 짜기도 전에 운영 문서가 너무 많아졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능을 만들기도 전에 라우팅 구조를 이해해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;무엇을 구현할까?&quot;보다 &quot;어떤 agent가 어떤 skill을 읽어야 하지?&quot;를 먼저 생각하게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그때 깨달았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하네스는 처음부터 크게 짓는 것이 아니라, 반복되는 고통이 확인된 뒤에 붙여야 한다는 것.&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;하네스 엔지니어링이란 무엇인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 여기서 말하는 하네스는 AI 코딩 에이전트가 일을 잘하도록 둘러싸는 작업 환경이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 프롬프트를 잘 쓰는 것과는 조금 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 하네스에는 이런 것들이 포함된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트가 처음 읽어야 할 문서&lt;/li&gt;
&lt;li&gt;프로젝트의 목표와 하지 말아야 할 것&lt;/li&gt;
&lt;li&gt;작업을 나누는 기준&lt;/li&gt;
&lt;li&gt;이슈와 브랜치 생성 규칙&lt;/li&gt;
&lt;li&gt;커밋 메시지 규칙&lt;/li&gt;
&lt;li&gt;PR 템플릿&lt;/li&gt;
&lt;li&gt;리뷰 기준&lt;/li&gt;
&lt;li&gt;실패 기록 방식&lt;/li&gt;
&lt;li&gt;어떤 문서를 언제 읽을지 정하는 context loading 규칙&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트에게는 &quot;무엇을 하라&quot;는 명령만큼이나 &quot;무엇을 보게 할 것인가&quot;가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드레 카파시는 Software 3.0 시대를 이야기하며, 자연어 prompt가 새로운 형태의 프로그램처럼 작동한다고 설명했다. 관련 정리 글에서는 LLM을 일종의 운영체제에 비유하고, context window를 memory에 비유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 관점에서 보면 하네스 엔지니어링은 꽤 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM에게 어떤 context를 제공할지, 어떤 규칙을 고정할지, 어떤 판단은 사람에게 넘길지를 설계하는 일이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NeoLabHQ의 Context Engineering Kit이나 Muratcan Koylan의 Agent Skills for Context Engineering 같은 프로젝트들도 비슷한 문제의식에서 출발한다. 핵심은 agent가 긴 대화와 많은 문서 속에서 길을 잃지 않도록, 필요한 context만 고르고 관리하게 만드는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 하네스는 분명히 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 내가 직접 겪어보니 중요한 질문은 따로 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;언제부터, 얼마나 적용할 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;내가 겪은 문제: 프로젝트보다 하네스가 먼저 커졌다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 만든 구조는 기능적으로는 돌아갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Claude에게 이런 식으로 명령할 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;CLAUDE.md를 기준으로 시작해.
.claude/PROJECT.md와 command-router를 읽어.
No-Training MVP를 8개 작업으로 나눠줘.
Issue-first, worktree-first 흐름으로 진행해.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 Claude는 작업을 잘 나눴다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;T1 입력 영상/프레임 샘플링
T2 SAM/SAM2 segmentation adapter
T3 depth/pose adapter mock 또는 interface
T4 masked back-projection
T5 object point cloud fusion
T6 oriented bbox 및 크기 측정
T7 evaluation log
T8 Open3D/Rerun visualization&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 첫 번째 구현 작업이 사실 그렇게 거창하지 않았다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;T1은 단순히 입력 영상에서 프레임을 샘플링하고, 후속 단계가 사용할 manifest를 만드는 작업이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도라면 처음에는 아래 정도 구조로도 충분했다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;README.md
docs/PLAN.md
docs/DECISIONS.md
src/
tests/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 나는 처음부터 agents, skills, rules, tasks, worktree, PR orchestration까지 붙였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 보니 프로젝트 초반부터 이런 비용이 생겼다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서를 이해하는 데 시간이 든다.&lt;/li&gt;
&lt;li&gt;Claude가 읽어야 할 파일이 많아진다.&lt;/li&gt;
&lt;li&gt;토큰 사용량이 커진다.&lt;/li&gt;
&lt;li&gt;실제 구현보다 운영 구조를 관리하는 시간이 늘어난다.&lt;/li&gt;
&lt;li&gt;내가 직접 프로젝트를 이해하기도 전에 자동화 구조를 이해해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하네스가 나쁜 것은 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하네스를 너무 일찍, 너무 많이 붙인 것이 문제였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자동화는 반복된 뒤에 붙여야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 경험을 통해 기준을 하나 세웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자동화는 반복되는 고통이 확인된 뒤에 붙인다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동화를 붙이기 좋은 순간은 대략 이런 경우다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 같은 일을 3번 이상 반복했다.
2. 실수했을 때 비용이 크다.
3. 형식이 거의 고정되어 있다.
4. 사람이 매번 판단하지 않아도 된다.
5. 여러 작업자가 동시에 움직여 충돌 위험이 생겼다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 PR 템플릿은 초반부터 있어도 괜찮다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR을 만들 때마다 작업 요약, 관련 이슈, 변경 내용, 리뷰 포인트, 체크리스트를 적는 일은 반복적이고 형식도 고정되어 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 multi-agent orchestration은 처음부터 크게 넣을 필요가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 병렬 작업이 생기고, 같은 파일을 두 agent가 건드리고, 병합 순서를 고민해야 하는 상황이 왔을 때 도입해도 늦지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패 기록 시스템도 마찬가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 거대한 failure memory 구조를 만들기보다, 같은 실패를 두세 번 반복했을 때 그때 durable note로 남기는 편이 더 자연스럽다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다음 프로젝트에서는 어떻게 시작할 것인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 프로젝트에서는 처음부터 큰 하네스를 만들지 않을 생각이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 아주 얇게 시작할 것이다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;README.md
CLAUDE.md
docs/
  PLAN.md
  DECISIONS.md
  LOG.md
src/
tests/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;code&gt;CLAUDE.md&lt;/code&gt;에는 딱 이 정도만 적을 것 같다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# 작업 원칙

1. README.md와 docs/PLAN.md를 먼저 읽는다.
2. 현재 목표를 한 문장으로 확인한다.
3. 작업 범위를 작게 잡는다.
4. 파일을 만들기 전에 기존 구조를 확인한다.
5. 테스트 가능한 단위로 끝낸다.
6. 반복되는 절차가 3번 이상 나오면 자동화 후보로 docs/DECISIONS.md에 기록한다.
7. 새 agents/skills/rules 폴더는 사용자 승인 전 만들지 않는다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도면 초반 MVP에는 충분하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 한두 기능은 사람이 직접 흐름을 경험해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래야 진짜 병목이 보인다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매번 테스트 명령을 까먹는가?&lt;/li&gt;
&lt;li&gt;PR 설명 작성이 반복되는가?&lt;/li&gt;
&lt;li&gt;실험 로그 형식이 흔들리는가?&lt;/li&gt;
&lt;li&gt;데이터 경로가 자주 꼬이는가?&lt;/li&gt;
&lt;li&gt;리뷰 기준이 매번 달라지는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 고통이 실제로 확인되면 그때 자동화하면 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;하네스를 없애자는 뜻은 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 하네스를 만들지 말자는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 AI 에이전트를 오래 쓰려면 하네스는 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 하네스를 처음부터 완성형으로 만들 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 앞으로 하네스를 이렇게 바라보려고 한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;하네스는 프로젝트를 시작하기 위한 선행 조건이 아니다.
하네스는 프로젝트가 자라면서 생기는 반복 문제를 해결하는 장치다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 처음부터 모든 것을 자동화하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 직접 해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 부분을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 부분만 작게 자동화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다시 프로젝트를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 반복하면서 하네스가 자라게 둔다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;지금 프로젝트에 적용한다면?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 진행 중인 컴퓨터 비전 프로젝트에는 이미 꽤 큰 &lt;code&gt;.claude/&lt;/code&gt; 하네스가 만들어져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이걸 아예 폐기할까도 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 지금은 완전 삭제보다 다른 방식이 더 낫다고 생각한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;기존 하네스는 보관한다.
기본 작업 흐름에서는 읽지 않는다.
필요할 때만 꺼내 쓴다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;code&gt;.claude/&lt;/code&gt;는 고급 하네스 archive처럼 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 작업은 더 가볍게 간다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;프로젝트/
├── .claude/
│   ├── agents/          # 에이전트 정의 파일
│   │   ├── analyst.md
│   │   ├── builder.md
│   │   └── qa.md
│   └── skills/          # 스킬 파일
│       ├── analyze/
│       │   └── SKILL.md
│       └── build/
│           ├── SKILL.md
│           └── references/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 필요할 때 폴더나 문서를 추가하면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 작업을 3번 이상 반복할 때, skills에 해당 스킬을 만들어 자동화한다.&lt;/li&gt;
&lt;li&gt;PR 충돌이나 병합 순서를 판단해야 할 때, agents에 PR충돌 전용 agent를 명시한다.&lt;/li&gt;
&lt;li&gt;리뷰 기준이 흔들릴 때, 리뷰 기준을 하나씩 추가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 지금까지 만든 하네스를 버리지 않으면서도, 초반 MVP의 속도를 잃지 않을 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론: 좋은 하네스는 작게 시작한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 경험으로 하네스 엔지니어링을 조금 다르게 보게 됐다. 하네스의 목적은 멋진 agent 시스템을 만드는 것이 아니다. 문서를 많이 만드는 것도 아니다. 처음부터 모든 과정을 자동화하는 것도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하네스의 목적은 인간의 집중력을 지키는 것이다. AI가 코드를 빠르게 만들 수 있게 되면서 병목은 코딩 속도에서 검증, 맥락 관리, 의사결정으로 옮겨가고 있다. 그래서 context를 설계하는 일은 분명 중요하다. 하지만 중요한 것과 처음부터 크게 하는 것은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 얻은 결론은 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는&amp;nbsp;수동으로&amp;nbsp;작게&amp;nbsp;만든다.&lt;br /&gt;반복되는&amp;nbsp;고통을&amp;nbsp;관찰한다.&lt;br /&gt;그&amp;nbsp;고통만&amp;nbsp;자동화한다.&lt;br /&gt;구조는&amp;nbsp;필요해질&amp;nbsp;때&amp;nbsp;자라게&amp;nbsp;둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하네스 엔지니어링은 자동화를 많이 붙이는 기술이 아니라, &lt;b&gt;언제 자동화하지 않을지를 아는 기술&lt;/b&gt;에 더 가깝다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://alanhou.org/blog/yc-andrej-karpathy-software-changing/&quot;&gt;Notes: Software Is Changing (Again)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/NeoLabHQ/context-engineering-kit&quot;&gt;NeoLabHQ Context Engineering Kit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://yuv.ai/blog/agent-skills-for-context-engineering&quot;&gt;Agent Skills: Context Engineering Framework for AI Agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://snyk.io/fr/articles/is-vibe-coding-secure/&quot;&gt;Vibe coding과 Karpathy의 문제 제기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;태그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;AI코딩&lt;/code&gt; &lt;code&gt;ClaudeCode&lt;/code&gt; &lt;code&gt;Codex&lt;/code&gt; &lt;code&gt;ContextEngineering&lt;/code&gt; &lt;code&gt;하네스엔지니어링&lt;/code&gt; &lt;code&gt;VibeCoding&lt;/code&gt; &lt;code&gt;개발회고&lt;/code&gt;&lt;/p&gt;</description>
      <category>AI</category>
      <category>AI</category>
      <category>AI코딩</category>
      <category>harness</category>
      <category>개발회고</category>
      <category>카파시</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/275</guid>
      <comments>https://wondrous-developer.tistory.com/275#entry275comment</comments>
      <pubDate>Thu, 21 May 2026 16:15:01 +0900</pubDate>
    </item>
    <item>
      <title>[OS] 4. Thread(쓰레드)란?</title>
      <link>https://wondrous-developer.tistory.com/274</link>
      <description>&lt;h1&gt;Lecture 4. Threads &amp;amp; Concurrency&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread를 공부하면서 단순히 &quot;프로세스보다 가볍다&quot;는 말을 외우기보다, &lt;b&gt;왜 Thread가 등장했는지&lt;/b&gt;, &lt;b&gt;프로세스와 무엇이 같고 무엇이 다른지&lt;/b&gt;를 이해하는 데 집중했다. 이 글은 그 과정을 정리한 기록이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Thread가 왜 필요한가?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 서버로 이해하는 문제 상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버는 동시에 수많은 클라이언트의 요청을 처리해야 한다. 클라이언트가 &lt;code&gt;http://www.naver.com/index.html&lt;/code&gt;을 요청하면, 서버는 네트워크에서 요청을 받아 CPU로 처리하고 디스크에서 파일을 읽어 응답한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 이 과정에서 디스크 I/O가 발생한다는 것이다. I/O 작업 중에는 서버가 &lt;b&gt;blocked&lt;/b&gt; 상태가 되어 다른 클라이언트의 요청을 처리할 수 없다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로세스를 여러 개 만들면 안 되나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직관적인 해결책은 클라이언트마다 서버 프로세스를 하나씩 fork()하는 것이다. 하지만 이 방법에는 두 가지 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 &lt;b&gt;메모리 낭비&lt;/b&gt;다. 모든 서버 프로세스는 동일한 코드를 실행하지만 fork()는 Code, Data, Stack, Heap을 모두 복사하므로 N개의 프로세스를 만들면 거의 동일한 메모리가 N벌 존재하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 &lt;b&gt;생성 시간&lt;/b&gt;이다. 프로세스 생성은 메모리 복사와 PCB 초기화 등 상당한 비용이 든다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Thread가 해결하는 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread는 프로세스 안에서 실행 흐름만 독립적으로 분리한 것이다. 같은 프로세스 안의 여러 스레드는 Code, Data, Heap을 &lt;b&gt;공유&lt;/b&gt;하면서, Stack과 Registers와 Thread ID만 &lt;b&gt;독립적으로&lt;/b&gt; 가진다. 서버 프로세스 하나에 N개의 스레드를 만들면, 코드와 데이터는 공유하므로 메모리를 N벌 복사할 필요가 없다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Thread의 정의와 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread는 &lt;b&gt;CPU 활용의 기본 단위(a basic unit of CPU utilization)&lt;/b&gt; 다. 각 프로세스는 하나 이상의 스레드를 포함할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 프로세스의 스레드들이 &lt;b&gt;공유&lt;/b&gt;하는 것은 Code, Data, Heap, Open files, Signal handlers, Working environment(현재 디렉토리, user ID 등)다. 각 스레드가 &lt;b&gt;독립적&lt;/b&gt;으로 갖는 것은 Stack, Registers, Thread ID다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack이 독립적인 이유는 각 스레드가 서로 다른 실행 흐름을 가지기 때문이다. 함수 호출 순서와 지역 변수는 스레드마다 달라야 하므로 Stack은 공유할 수 없다. Registers가 독립적인 이유는 각 스레드가 CPU를 받았을 때 자신이 어디까지 실행했는지(Program Counter 포함)를 저장해야 하기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l4_d1_thread_struct.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;950&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d3nAMp/dJMcabxhkYC/hyMI5VzFBoHmPirPGlA821/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d3nAMp/dJMcabxhkYC/hyMI5VzFBoHmPirPGlA821/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d3nAMp/dJMcabxhkYC/hyMI5VzFBoHmPirPGlA821/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd3nAMp%2FdJMcabxhkYC%2FhyMI5VzFBoHmPirPGlA821%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;950&quot; data-filename=&quot;l4_d1_thread_struct.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;950&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Thread vs Process&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Thread 생성 방식 비교&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork()는 메모리 전체를 복사하지만, pthread_create()는 Stack만 새로 할당하고 나머지는 기존 프로세스와 공유한다. 그래서 Thread를 &lt;b&gt;Lightweight Process(경량 프로세스)&lt;/b&gt; 라고 부르기도 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l4_d2_fork_vs_thread.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bygY3C/dJMcahj1mbg/V4NdtvjBSr0APrGBsgDgRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bygY3C/dJMcahj1mbg/V4NdtvjBSr0APrGBsgDgRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bygY3C/dJMcahj1mbg/V4NdtvjBSr0APrGBsgDgRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbygY3C%2FdJMcahj1mbg%2FV4NdtvjBSr0APrGBsgDgRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;900&quot; data-filename=&quot;l4_d2_fork_vs_thread.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Thread를 사용하는 네 가지 이점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Responsiveness(응답성) 는 하나의 스레드가 I/O로 blocked되어도 다른 스레드는 계속 실행된다는 점이다.&lt;/li&gt;
&lt;li&gt;Resource Sharing(자원 공유) 은 같은 프로세스 내의 스레드들이 메모리와 자원을 자동으로 공유한다는 점이다. 단, 이것이 Race Condition의 원인이 되기도 한다.&lt;/li&gt;
&lt;li&gt;Economy(경제성) 는 프로세스 생성과 Context Switch의 오버헤드가 스레드보다 훨씬 크다는 점이다. 스레드는 Stack만 새로 할당하면 되므로 생성 비용이 낮다.&lt;/li&gt;
&lt;li&gt;Utilization of MP Architectures(멀티프로세서 활용) 는 멀티코어 CPU에서 여러 스레드를 각 코어에 분산하면 진정한 병렬 처리가 가능하다는 점이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로세스와 스레드의 트레이드오프&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;th&gt;프로세스&lt;/th&gt;
&lt;th&gt;스레드&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;메모리 격리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;완전 격리&lt;/td&gt;
&lt;td&gt;메모리 공유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;안정성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;한 프로세스 죽어도 OK&lt;/td&gt;
&lt;td&gt;하나 죽으면 전체 영향&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;생성 비용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Context Switch&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;비쌈&lt;/td&gt;
&lt;td&gt;저렴함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;통신 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;IPC 필요&lt;/td&gt;
&lt;td&gt;메모리 직접 공유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Race Condition&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;발생 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Concurrency vs Parallelism&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티코어 프로그래밍을 이해하려면 이 두 개념을 구분해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Concurrency(동시성) 는 여러 작업이 진행 중인 상태를 말한다. 싱글 코어에서도 스케줄러가 빠르게 Context Switch를 하면 여러 스레드가 동시에 진행되는 것처럼 보인다.&lt;/li&gt;
&lt;li&gt;Parallelism(병렬성) 은 여러 작업이 물리적으로 동시에 실행되는 것이다. 멀티코어 시스템에서만 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글 코어는 한 순간에 하나의 스레드만 실행하지만 빠르게 교체하여 동시에 진행되는 것처럼 보인다. 멀티 코어는 여러 스레드가 각 코어에서 물리적으로 동시에 실행된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;멀티코어 프로그래밍의 어려움&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티코어 환경에서 스레드를 효율적으로 사용하려면 Dividing activities(작업을 독립 단위로 분리), Balance(코어에 균등 분배), Data splitting(데이터 분할), Data dependency(작업 순서 보장), Testing and debugging(비결정적 실행 순서로 재현 어려움) 등의 문제를 해결해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l4_d3_concurrency_parallelism.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kejx6/dJMcafT04TE/hT1VcJOlRe3jfGMCBa5TGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kejx6/dJMcafT04TE/hT1VcJOlRe3jfGMCBa5TGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kejx6/dJMcafT04TE/hT1VcJOlRe3jfGMCBa5TGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKejx6%2FdJMcafT04TE%2FhT1VcJOlRe3jfGMCBa5TGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;900&quot; data-filename=&quot;l4_d3_concurrency_parallelism.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. User Threads vs Kernel Threads&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드는 누가 관리하느냐에 따라 두 종류로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;User Thread 는 사용자 수준 라이브러리가 관리하는 스레드다. OS 커널은 이 스레드의 존재를 모른다. POSIX pthreads, Win32 threads, Java threads가 대표적이다. 커널을 거치지 않으므로 생성과 Context Switch가 빠르다. 하지만 한 스레드가 System Call(I/O 등)로 blocking되면 커널은 프로세스 전체를 block시키기 때문에 다른 스레드들도 모두 멈춘다.&lt;/li&gt;
&lt;li&gt;Kernel Thread 는 운영체제가 직접 관리하는 스레드다. Windows, Linux, Mac OS X 등 현대 OS 대부분이 지원한다. 커널이 각 스레드를 독립적으로 스케줄링한다. 한 스레드가 blocking되어도 다른 스레드는 계속 실행된다. 대신 스레드 생성과 Context Switch에 System Call이 필요하므로 User Thread보다 비용이 더 든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l4_d4_user_kernel_thread.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;950&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciRubE/dJMcahRNFR0/DePpuGb3XExKEZLRzasOj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciRubE/dJMcahRNFR0/DePpuGb3XExKEZLRzasOj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciRubE/dJMcahRNFR0/DePpuGb3XExKEZLRzasOj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciRubE%2FdJMcahRNFR0%2FDePpuGb3XExKEZLRzasOj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;950&quot; data-filename=&quot;l4_d4_user_kernel_thread.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;950&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Linux Threads&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux는 스레드와 프로세스를 별도로 구분하지 않는다. 대신 &lt;code&gt;clone()&lt;/code&gt; 시스템 콜로 &quot;얼마나 공유할지&quot;를 플래그로 지정해서 생성하며, 공유 범위에 따라 프로세스에 가까워지기도, 스레드에 가까워지기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플래그를 많이 설정할수록 스레드에 가깝고, 적게 설정할수록 프로세스에 가깝다. &lt;code&gt;clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0)&lt;/code&gt;은 스레드에 가깝고, &lt;code&gt;clone(CLONE_SIGHAND, 0)&lt;/code&gt;은 프로세스에 가깝다. 스레드에 가깝게 clone()하면 두 task_struct가 mm(메모리), sighand(시그널 핸들러), fs(파일 시스템), files(열린 파일)를 모두 가리키는 포인터를 공유하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, fork()와 clone(thread)의 차이는 자식이 부모의 자원을 &lt;b&gt;복사&lt;/b&gt;하느냐, &lt;b&gt;공유&lt;/b&gt;하느냐의 차이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l4_d5_linux_clone.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7tuRy/dJMcadaKw9w/bmNg1q6gUUatpYmvKtIfZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7tuRy/dJMcadaKw9w/bmNg1q6gUUatpYmvKtIfZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7tuRy/dJMcadaKw9w/bmNg1q6gUUatpYmvKtIfZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7tuRy%2FdJMcadaKw9w%2FbmNg1q6gUUatpYmvKtIfZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;1000&quot; data-filename=&quot;l4_d5_linux_clone.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread는 CPU 활용의 기본 단위로, 프로세스 안에서 Code&amp;middot;Data&amp;middot;Heap을 공유하며 Stack&amp;middot;Registers&amp;middot;Thread ID만 독립적으로 가진다. 이 공유 덕분에 생성 비용이 낮고 통신이 쉽지만, Race Condition의 원인이 되기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concurrency는 싱글 코어에서 빠른 Context Switch로 동시 진행처럼 보이는 것이고, Parallelism은 멀티코어에서 물리적으로 동시 실행되는 것이다. User Thread는 빠르지만 blocking 문제가 있고, Kernel Thread는 안전하지만 비용이 높다. Linux는 clone() 플래그로 공유 범위를 조절하여 이 둘을 하나의 메커니즘으로 통합한다.&lt;/p&gt;</description>
      <category>Computer Science/OS(운영 체제)</category>
      <category>CS</category>
      <category>OS</category>
      <category>운영체제</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/274</guid>
      <comments>https://wondrous-developer.tistory.com/274#entry274comment</comments>
      <pubDate>Sun, 3 May 2026 21:58:07 +0900</pubDate>
    </item>
    <item>
      <title>[OS] 3. 프로세스(Process)란?</title>
      <link>https://wondrous-developer.tistory.com/273</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Process를 공부하면서 단순히 &quot;실행 중인 프로그램&quot;이라는 정의를 외우기보다, &lt;b&gt;프로그램이 어떻게 프로세스가 되는지&lt;/b&gt;, &lt;b&gt;OS가 프로세스를 어떻게 메모리에 프로세스 자원을 할당하고 관리하는지&lt;/b&gt;, 그리고 &lt;b&gt;프로세스들이 어떻게 협력하는지&lt;/b&gt;를 이해하는 데 집중했다. 이 글은 그 과정을 정리한 기록입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 프로세스란 무엇인가?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로그램 vs 프로세스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램은 디스크에 저장된 정적인 파일이다. 실행 코드가 담긴 실행 파일(binary)일 뿐이며, 그 자체로는 아무 일도 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스는 &lt;b&gt;실행 중인 프로그램(program in execution)&lt;/b&gt;이다. 프로그램이 메모리에 적재되어 CPU가 실제로 명령어를 실행하기 시작하면 프로세스가 된다. 즉 같은 프로그램을 두 번 실행하면 프로세스가 두 개 생긴다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로세스의 메모리 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l3_memory_layout.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buWaYZ/dJMcafmbeaR/xFN7D2Xll8uqUQM9vqRNok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buWaYZ/dJMcafmbeaR/xFN7D2Xll8uqUQM9vqRNok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buWaYZ/dJMcafmbeaR/xFN7D2Xll8uqUQM9vqRNok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuWaYZ%2FdJMcafmbeaR%2FxFN7D2Xll8uqUQM9vqRNok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;1150&quot; data-filename=&quot;l3_memory_layout.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스가 메모리에 올라갈 때 주소 공간은 4개 영역으로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Text (Code) 영역:&amp;nbsp;실행할 프로그램의 기계어 명령어가 저장되는 곳. 컴파일된 코드 자체가 여기 들어가고, 실행 중에 내용이 바뀌면 안 되므로 Read-Only로 보호된다. 여러 프로세스가 같은 프로그램을 실행하면 이 영역을 공유할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Data 영역:&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;b&gt;전역 변수(global variable)와 정적 변수(static variable)&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; 가 저장된다. 프로그램 시작 시 할당되고 종료 시 해제. 초기값이 있는 변수(initialized)와 없는 변수(uninitialized, BSS)로 세분화된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Heap 영역:&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;b&gt;동적으로 할당되는 메모리&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; 공간. C의 malloc(), C++의 new, Java의 new 로 할당하는 메모리가 여기 들어가며 아래에서 위로(&amp;uarr;) 자랍니다. 프로그래머가 직접 해제(free())하지 않으면 &lt;/span&gt;&lt;b&gt;메모리 누수(Memory Leak)&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; 가 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Stack 영역:&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;b&gt;함수 호출 정보&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;가 저장되는 공간. 함수를 호출할 때마다 &lt;/span&gt;&lt;b&gt;Stack Frame&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; 하나가 쌓이고, 함수가 반환되면 사라짐. Stack Frame에는 지역 변수, 매개변수, 리턴 주소가 들어가며 위에서 아래로(&amp;darr;) 자란다. 재귀 호출이 너무 깊어지면 Heap과 충돌해 &lt;/span&gt;&lt;b&gt;Stack Overflow&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;가 발생한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 나올 내용이지만, 멀티스레드 관점에서 같은 프로세스의 스레드들은 Text, Data, Heap을 &lt;b&gt;공유&lt;/b&gt;하고 Stack, Registers, Thread ID는 &lt;b&gt;독립적&lt;/b&gt;으로 가진다. Stack이 독립적인 이유는 각 스레드가 자신만의 실행 흐름과 지역 변수를 가져야 하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Virtual Memory, Physical Memory&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l3_virtual_memory.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bg8KaZ/dJMcagSU6Cf/zK14pgWGD5WmGjFgcD5fK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bg8KaZ/dJMcagSU6Cf/zK14pgWGD5WmGjFgcD5fK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bg8KaZ/dJMcagSU6Cf/zK14pgWGD5WmGjFgcD5fK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbg8KaZ%2FdJMcagSU6Cf%2FzK14pgWGD5WmGjFgcD5fK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1400&quot; data-filename=&quot;l3_virtual_memory.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스가 사용하는 메모리 주소는 사실 실제 물리 메모리의 주소가 아니다. OS는 각 프로세스에게 &lt;b&gt;Virtual Address Space(가상 주소 공간)&lt;/b&gt; 를 제공한다. 프로세스 입장에서는 자기가 메모리 전체를 독점하는 것처럼 보이지만, 실제로는 여러 프로세스가 &lt;b&gt;Physical Memory(물리 메모리)&lt;/b&gt; 를 나눠 쓰고 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Virtual Memory(가상 메모리)&lt;/b&gt; 는 프로세스가 바라보는 논리적인 주소 공간이다. 각 프로세스는 0번지부터 시작하는 독립적인 가상 주소 공간을 갖는다. 실제로 메모리가 얼마나 있는지, 다른 프로세스가 어디에 올라가 있는지 전혀 신경 쓸 필요가 없다. 덕분에 프로그램 작성이 단순해지고, 프로세스 간 메모리 침범이 불가능해진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Physical Memory(물리 메모리)&lt;/b&gt; 는 실제 DRAM 칩 위의 주소 공간이다. 여러 프로세스의 가상 메모리가 이곳에 섞여 적재된다. 물리 메모리가 부족하면 OS는 일부 페이지를 디스크(Swap Space)로 내보낸다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Page Table(페이지 테이블)&lt;/b&gt; 은 가상 주소와 물리 주소를 연결하는 변환 지도다. 가상 메모리를 일정 크기(보통 4KB)의 &lt;b&gt;Page&lt;/b&gt; 단위로 나누고, 물리 메모리도 같은 크기의 &lt;b&gt;Frame&lt;/b&gt; 단위로 나눈 뒤, Page Table이 &quot;가상 Page N &amp;rarr; 물리 Frame M&quot; 매핑을 저장한다. CPU가 가상 주소로 메모리에 접근할 때마다 &lt;b&gt;MMU(Memory Management Unit)&lt;/b&gt; 가 Page Table을 참조해 자동으로 물리 주소로 변환한다. 만약 접근하려는 가상 Page가 물리 메모리에 없으면(디스크에 있거나 아직 할당 안 됨) &lt;b&gt;Page Fault&lt;/b&gt; 가 발생하고, OS가 해당 Page를 디스크에서 물리 메모리로 불러온다. 이것이 COW에서 쓰기 시도 시 Page Fault가 발생하는 원리이기도 하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Vertual Machine와의 유사성&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 자원은 하나인데, 각 주체가 독점하는 것처럼 착각하게 만든다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vertual Memoy는 &amp;ldquo;가상화&amp;rdquo;라는 관점에서 이 전 글에서 다루었던 Vertual Macine(가상 컴퓨터)와 비슷한 원리이다. 즉 &lt;b&gt;추상화 대상의 레벨&lt;/b&gt;만 다를 뿐, 핵심 원리는 똑같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Virtual Memory&lt;/b&gt; 는 하나의 OS 안에서 &lt;b&gt;프로세스에게&lt;/b&gt; &quot;너만의 메모리가 있는 척&quot; 해주는 것&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Virtual Machine&lt;/b&gt; 은 하나의 하드웨어 위에서 &lt;b&gt;OS 자체에게&lt;/b&gt; &quot;너만의 컴퓨터가 있는 척&quot; 해주는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 프로세스 상태&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5가지 상태와 상태 전이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS는 프로세스를 5가지 상태로 관리한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l3_state_diagram.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYTBFq/dJMcab48iwy/0MuC3ux2FxUsvoaKd7NBb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYTBFq/dJMcab48iwy/0MuC3ux2FxUsvoaKd7NBb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYTBFq/dJMcab48iwy/0MuC3ux2FxUsvoaKd7NBb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYTBFq%2FdJMcab48iwy%2F0MuC3ux2FxUsvoaKd7NBb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;1000&quot; data-filename=&quot;l3_state_diagram.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;New&lt;/b&gt;: 프로세스가 생성되고 있는 상태. PCB 할당, 메모리 할당 등 초기화 진행 중&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ready&lt;/b&gt;: CPU를 받을 준비가 된 상태. Ready Queue에서 스케줄러를 기다린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Running&lt;/b&gt;: CPU를 받아 명령어를 실행 중인 상태. 단일 코어에서는 한 번에 하나의 프로세스만 Running 상태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Waiting (Blocked)&lt;/b&gt;: I/O 완료, 이벤트 발생 등을 기다리는 상태. CPU를 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Terminated&lt;/b&gt;: 실행이 끝난 상태. 자원은 해제되지만 PCB는 부모가 wait()를 호출할 때까지 남는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CPU 스케줄링이 발생하는 4가지 상황&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Running &amp;rarr; Waiting&lt;/b&gt; (예: I/O 요청) &amp;mdash; &lt;b&gt;비선점(Non-preemptive)&lt;/b&gt;: 프로세스가 자발적으로 CPU 반납&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Running &amp;rarr; Ready&lt;/b&gt; (예: Time Slice 소진) &amp;mdash; &lt;b&gt;선점(Preemptive)&lt;/b&gt;: OS가 강제로 CPU 회수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Waiting &amp;rarr; Ready&lt;/b&gt; (예: I/O 완료) &amp;mdash; &lt;b&gt;선점(Preemptive)&lt;/b&gt;: 우선순위가 바뀌어 재스케줄링 필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Running &amp;rarr; Terminated&lt;/b&gt; (프로세스 종료) &amp;mdash; &lt;b&gt;비선점(Non-preemptive)&lt;/b&gt;: 자발적 종료&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비선점이라고 해서 CPU가 노는 게 아니다. 자발적 반납 이후에도 스케줄러는 즉시 Ready Queue에서 다음 프로세스를 선택한다. 선점/비선점은 CPU를 &lt;b&gt;어떻게 회수했는지&lt;/b&gt;의 문제이고, 스케줄링은 &lt;b&gt;비어있는 CPU를 누구한테 줄지&lt;/b&gt;의 문제로 서로 다른 차원이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. PCB와 Context Switch&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PCB (Process Control Block)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l3_d3_pcb.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DI0dU/dJMcaipFecq/PSpAXsVW3yuo3axoDqHoQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DI0dU/dJMcaipFecq/PSpAXsVW3yuo3axoDqHoQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DI0dU/dJMcaipFecq/PSpAXsVW3yuo3axoDqHoQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDI0dU%2FdJMcaipFecq%2FPSpAXsVW3yuo3axoDqHoQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;1150&quot; data-filename=&quot;l3_d3_pcb.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 OS가 프로세스를 관리하기 위해 유지하는 자료구조다. TCB(Task Control Block)는 PCB와 동일한 개념으로 이름만 다르다. Linux에서는 &lt;code&gt;task_struct&lt;/code&gt; 구조체로 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PCB에 포함되는 정보는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Process ID (PID)&lt;/b&gt;: 프로세스 고유 식별자&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Process State&lt;/b&gt;: 현재 상태 (New, Ready, Running, Waiting, Terminated)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Program Counter (PC)&lt;/b&gt;: 다음에 실행할 명령어 주소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CPU Registers&lt;/b&gt;: 레지스터 값들 (Context Switch 시 저장/복원)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Memory Management Info&lt;/b&gt;: 페이지 테이블, 메모리 할당 정보&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Scheduling Info&lt;/b&gt;: 우선순위, 스케줄링 큐 포인터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;I/O Status&lt;/b&gt;: 열린 파일 목록, I/O 장치 정보&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Accounting Info&lt;/b&gt;: CPU 사용 시간, 실행 시간 등&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Context Switch&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l3_context_switch.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckyiDs/dJMcabqyz2g/Tfw7y0pe65zDRJwilPdzTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckyiDs/dJMcabqyz2g/Tfw7y0pe65zDRJwilPdzTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckyiDs/dJMcabqyz2g/Tfw7y0pe65zDRJwilPdzTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckyiDs%2FdJMcabqyz2g%2FTfw7y0pe65zDRJwilPdzTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1900&quot; height=&quot;1250&quot; data-filename=&quot;l3_context_switch.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Switch는 CPU가 현재 실행 중인 프로세스에서 다른 프로세스로 전환하는 과정이다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;프로세스 P1 실행 중
  &amp;darr; 인터럽트 발생 (I/O 요청 or Timer Interrupt)
1. P1의 CPU 상태 &amp;rarr; P1의 PCB에 저장 (PC, Registers 등)
2. 스케줄러: Ready Queue에서 P2 선택
3. P2의 PCB에서 CPU 상태 복원
  &amp;darr;
프로세스 P2 실행 시작&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Switch 중에는 CPU가 유용한 작업을 못하므로 &lt;b&gt;순수한 오버헤드&lt;/b&gt;다. 이 비용을 Dispatch Latency라고 하며 약 1~10&amp;mu;s 정도 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로, Thread가 Process보다 Context Switch 비용이 낮은 이유는 메모리 공간이 공유되어 있어 교체할 정보가 적기 때문이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 프로세스 생성과 종료&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로세스 생성 - fork()와 COW&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l3_d6_fork_cow.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2nIH0/dJMcajvjop6/EbOIEULkmAt6kLe0KnrYlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2nIH0/dJMcajvjop6/EbOIEULkmAt6kLe0KnrYlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2nIH0/dJMcajvjop6/EbOIEULkmAt6kLe0KnrYlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2nIH0%2FdJMcajvjop6%2FEbOIEULkmAt6kLe0KnrYlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;1200&quot; data-filename=&quot;l3_d6_fork_cow.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unix/Linux에서 프로세스 생성은 &lt;code&gt;fork()&lt;/code&gt; 시스템 콜로 이루어진다. &lt;code&gt;fork()&lt;/code&gt;는 현재 프로세스(부모)를 복사해 새 프로세스(자식)를 만든다. &lt;code&gt;fork()&lt;/code&gt; 이후 자식 프로세스에서 &lt;code&gt;exec()&lt;/code&gt;를 호출하면 새 프로그램을 로드해 실행한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;(참고) fork vs vfork vs exec&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;fork로 복사하고, exec로 갈아엎고, vfork는 exec 직전의 불필요한 복사를 생략한 것&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l3_d5_fork_vfork_exec.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTY1Gx/dJMcac31hkX/LAE70ZdxNBfDjqFrcjQ5p1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTY1Gx/dJMcac31hkX/LAE70ZdxNBfDjqFrcjQ5p1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTY1Gx/dJMcac31hkX/LAE70ZdxNBfDjqFrcjQ5p1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTY1Gx%2FdJMcac31hkX%2FLAE70ZdxNBfDjqFrcjQ5p1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;1300&quot; data-filename=&quot;l3_d5_fork_vfork_exec.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;fork():&lt;/b&gt; 부모 프로세스를 &lt;b&gt;통째로 복사&lt;/b&gt;해서 자식 프로세스를 만든다. 위에서 설명한 COW(Copy-on-Write) 덕분에 실제 메모리 복사는 쓰기 시점까지 미룬다. 부모와 자식은 각자 독립적으로 실행을 이어나간다. &lt;code&gt;fork()&lt;/code&gt; 반환값이 0이면 자식, 양수(자식 PID)이면 부모다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;vfork():&lt;/b&gt; &lt;code&gt;fork()&lt;/code&gt;의 최적화 버전이다. 자식이 &lt;code&gt;exec()&lt;/code&gt; 또는 &lt;code&gt;exit()&lt;/code&gt;을 호출할 때까지 부모의 메모리를 &lt;b&gt;그대로 공유(복사 없음)&lt;/b&gt; 하고, 부모는 그 동안 &lt;b&gt;일시 정지&lt;/b&gt;된다. 어차피 &lt;code&gt;exec()&lt;/code&gt;으로 덮어쓸 메모리를 굳이 COW로 관리할 필요가 없을 때 쓰는 방식이다. 현대 OS에서는 &lt;code&gt;fork()&lt;/code&gt; + COW 자체가 충분히 빨라서 &lt;code&gt;vfork()&lt;/code&gt;를 쓸 일이 거의 없다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;exec():&lt;/b&gt; 현재 프로세스의 메모리 공간을 &lt;b&gt;새 프로그램으로 덮어쓴다&lt;/b&gt;. 새 프로세스를 만드는 게 아니라 기존 프로세스가 다른 프로그램으로 &lt;b&gt;변신&lt;/b&gt;하는 것이다. PID는 그대로 유지된다. 보통 &lt;code&gt;fork()&lt;/code&gt; 직후 자식 프로세스에서 호출하는 패턴(&lt;code&gt;fork&lt;/code&gt; + &lt;code&gt;exec&lt;/code&gt;)으로 쓰인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork() 직후 Virtual Memory는 독립적으로 분리되지만 실제 물리 메모리는 공유된다. Page Table Entry의 권한이 Read-Only로 설정되어 있어, 쓰기를 시도하면 Page Fault가 발생한다. 이것이 &lt;b&gt;COW(Copy-on-Write)&lt;/b&gt; 방식이다. OS는 Page Fault 발생 시 새 물리 페이지를 할당하고 데이터를 복사한 뒤 Page Table을 업데이트한다. 쓰기가 실제로 발생할 때만 물리 메모리를 분리하여 불필요한 메모리 복사를 방지한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로세스 종료 - exit, abort, wait&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스 종료에는 두 가지 방식이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정상 종료 (exit): 프로세스가 마지막 명령문을 실행하고 exit() 시스템 콜을 호출한다. 자식은 부모에게 exit status를 반환하고, OS는 프로세스의 자원을 해제한다.&lt;/li&gt;
&lt;li&gt;강제 종료 (abort): 부모 프로세스가 자식 프로세스를 강제로 종료시킬 수 있다. 자식이 할당받은 자원을 초과하여 사용하거나, 자식에게 시킨 작업이 더 이상 필요하지 않을 때 발생한다. kill() 같은 시스템 콜로 종료 신호를 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;wait()&lt;/b&gt;: 부모 프로세스가 자식 프로세스의 종료를 기다리는 시스템 콜이다. 부모는 &lt;code&gt;wait()&lt;/code&gt;를 호출하면 block 상태가 되어 자식이 &lt;code&gt;exit()&lt;/code&gt;으로 종료될 때까지 대기한다. 자식이 종료되면 exit status를 받아오고 OS는 자식의 PCB를 완전히 삭제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 OS는 &lt;b&gt;Cascading Termination&lt;/b&gt;을 지원한다. 부모가 종료되면 모든 자식, 손자 프로세스도 연쇄적으로 종료시키는 방식이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;좀비 프로세스와 고아 프로세스&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l3_d7_zombie_orphan.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckew6D/dJMcadojOwl/voWIZznleE4aEpYbQlu4Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckew6D/dJMcadojOwl/voWIZznleE4aEpYbQlu4Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckew6D/dJMcadojOwl/voWIZznleE4aEpYbQlu4Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckew6D%2FdJMcadojOwl%2FvoWIZznleE4aEpYbQlu4Gk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;1250&quot; data-filename=&quot;l3_d7_zombie_orphan.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좀비 프로세스(Zombie Process): 자식 프로세스가 종료(exit)되었지만 부모 프로세스가 wait()를 호출하지 않아, 자식의 PCB와 exit status가 프로세스 테이블에 남아있는 상태다. CPU나 메모리 같은 실제 자원은 이미 해제되었지만 프로세스 테이블의 슬롯을 차지한다. 부모가 나중에 wait()를 호출하거나 부모가 종료되면 정리된다.&lt;/li&gt;
&lt;li&gt;고아 프로세스(Orphan Process): 부모 프로세스가 자식보다 먼저 종료된 상태다. OS가 고아 프로세스를 init 프로세스(PID 1)에 입양시키고, init이 주기적으로 wait()를 호출하여 정리해주므로 문제가 되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Cooperating Processes와 IPC&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Independent vs Cooperating&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스는 다른 프로세스와의 관계에 따라 두 가지로 구분된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Independent Process(독립 프로세스): 다른 프로세스의 실행에 영향을 주거나 받지 않는 프로세스다. 완전히 독립적으로 실행하며 데이터를 공유하지 않는다.&lt;/li&gt;
&lt;li&gt;Cooperating Process(협력 프로세스): 다른 프로세스의 실행에 영향을 주거나 받을 수 있는 프로세스다. 서로 협력하여 작업을 수행하며, 이를 위해 IPC(프로세스 간 통신)가 필수다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cooperating Process를 사용하는 이유는 세 가지다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫째, &lt;b&gt;Information Sharing&lt;/b&gt;으로 여러 프로세스가 공유 파일 등의 정보를 함께 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;둘째, &lt;b&gt;Computation Speed-up&lt;/b&gt;으로 작업을 여러 subtask로 나눠 병렬 실행하면 처리 속도가 향상된다.&lt;/li&gt;
&lt;li&gt;셋째, &lt;b&gt;Modularity&lt;/b&gt;로 시스템 기능을 독립적인 작업들로 분리하여 구조화할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Producer-Consumer Pattern&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cooperating Process의 전형적인 예제로, 두 IPC 방식 모두로 구현 가능하지만 각 방식의 트레이드오프(속도 vs 안전성)에 따라 선택된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IPC (Inter-Process Communication)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cooperating Process들은 데이터를 주고받기 위해 IPC가 필요하다. IPC의 방식은 두 가지다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l3_d4b_ipc.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;975&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0rTm7/dJMcagMayU5/dzClV4Zw6KJKWHV272HG4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0rTm7/dJMcagMayU5/dzClV4Zw6KJKWHV272HG4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0rTm7/dJMcagMayU5/dzClV4Zw6KJKWHV272HG4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0rTm7%2FdJMcagMayU5%2FdzClV4Zw6KJKWHV272HG4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;975&quot; data-filename=&quot;l3_d4b_ipc.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;975&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) Shared Memory (공유 메모리)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스들이 같은 메모리 영역을 공유한다. 직접 메모리를 읽고 쓰므로 속도가 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여러 프로세스가 동시에 접근할 때 Race Condition이 발생할 수 있어 동기화가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Producer-Consumer Problem&lt;/b&gt;이 동기화 방법의 대표적인 예시로, Producer는 데이터를 공유 버퍼에 생산하고 Consumer는 버퍼에서 소비한다. 동기화를 위해 Semaphore, Mutex 같은 도구가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) Message Passing (메시지 전달)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS가 중간에서 메시지 전달을 관리한다. 프로세스들은 직접 메모리를 공유하지 않고 send()/receive() 시스템 콜로 통신한다. OS가 동기화를 자동으로 처리하므로 Race Condition이 발생하지 않는다. 대신 커널을 거치는 오버헤드가 있어 Shared Memory보다 느리다. Microkernel 구조에서 주로 사용하는 방식이다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;Shared Memory&lt;/th&gt;
&lt;th&gt;Message Passing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;빠름&lt;/td&gt;
&lt;td&gt;느림 (커널 개입)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;동기화&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;직접 처리 필요&lt;/td&gt;
&lt;td&gt;OS가 자동 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Race Condition&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;발생 가능&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Producer-Consumer&lt;/td&gt;
&lt;td&gt;Microkernel IPC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스는 실행 중인 프로그램으로, Text/Data/Heap/Stack 4개의 메모리 영역을 가진다. OS는 PCB를 통해 각 프로세스의 상태를 관리하며, Context Switch를 통해 프로세스 간 CPU 전환을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스 생성은 &lt;code&gt;fork()&lt;/code&gt; + COW 방식으로 이루어지며, 물리 메모리는 실제 쓰기가 발생할 때만 분리된다. 종료 시에는 &lt;code&gt;exit()&lt;/code&gt;으로 자발적으로 종료하거나 부모의 &lt;code&gt;abort()&lt;/code&gt;로 강제 종료되며, 부모는 &lt;code&gt;wait()&lt;/code&gt;로 자식의 종료를 기다려야 좀비 프로세스 발생을 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cooperating Process들은 Shared Memory 또는 Message Passing을 통해 협력한다. Shared Memory는 빠르지만 동기화 문제가 있고, Message Passing은 느리지만 OS가 동기화를 보장한다. 이 동기화 문제를 해결하기 위한 도구들이 이후 Synchronization에서 다루는 Mutex, Semaphore, Monitor다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/OS(운영 체제)</category>
      <category>CS</category>
      <category>OS</category>
      <category>운영체제</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/273</guid>
      <comments>https://wondrous-developer.tistory.com/273#entry273comment</comments>
      <pubDate>Wed, 29 Apr 2026 16:35:02 +0900</pubDate>
    </item>
    <item>
      <title>[OS] 2. Operating System Structures</title>
      <link>https://wondrous-developer.tistory.com/272</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 OS가 어떤 서비스를 제공하는지, 프로그램이 OS에게 어떻게 서비스를 요청하는지, 그리고 OS 내부가 어떤 구조로 설계되어 있는지를 다루어 보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. OS가 제공하는 서비스&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l2_d1_os_services.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0ObX2/dJMcajhIt04/nOowmHMmGAX9THKTGVmig0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0ObX2/dJMcajhIt04/nOowmHMmGAX9THKTGVmig0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0ObX2/dJMcajhIt04/nOowmHMmGAX9THKTGVmig0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0ObX2%2FdJMcajhIt04%2FnOowmHMmGAX9THKTGVmig0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1900&quot; height=&quot;1250&quot; data-filename=&quot;l2_d1_os_services.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS는 크게 두 가지 관점의 서비스를 제공한다. 하나는 사용자와 프로그램을 위한 서비스이고, 다른 하나는 시스템 자체의 효율적인 운영을 위한 서비스다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용자를 위한 서비스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;User Interface&lt;/b&gt;는 사용자가 OS와 상호작용하는 방법이다. CLI(Command Line Interface)와 GUI(Graphic User Interface) 두 가지가 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Program Execution&lt;/b&gt;은 프로그램을 메모리에 적재하고 실행하며 종료하는 역할이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;I/O Operations&lt;/b&gt;는 키보드&amp;middot;마우스 입력과 모니터&amp;middot;프린터 출력 등 I/O 장치를 제어한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;File System Manipulation&lt;/b&gt;은 파일과 디렉토리의 생성&amp;middot;삭제&amp;middot;읽기&amp;middot;쓰기&amp;middot;검색을 수행한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Communications&lt;/b&gt;는 프로세스 간 정보를 교환한다(Shared Memory 또는 Message Passing). &lt;b&gt;Error Detection&lt;/b&gt;은 하드웨어(메모리 오류, 전원 이상), I/O 장치(네트워크 연결 실패), 사용자 프로그램(산술 오버플로우, 잘못된 메모리 접근) 등의 오류를 감지한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시스템 효율을 위한 서비스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자에게 직접 보이지는 않지만 시스템이 효율적으로 동작하기 위한 서비스도 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Resource Allocation&lt;/b&gt;은 멀티태스킹 환경에서 CPU 사이클, 메모리, 파일 저장소, I/O 장치를 여러 사용자와 프로세스에 적절히 배분한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Accounting&lt;/b&gt;은 어떤 사용자가 얼마나 어떤 종류의 자원을 사용하는지 기록한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Protection and Security&lt;/b&gt;는 시스템 자원에 대한 접근을 제어하고(Protection), 사용자 인증과 비정상적인 접근 방어를 수행한다(Security).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. User Interface &amp;mdash; CLI vs GUI&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CLI(Command Line Interface)&lt;/b&gt; 는 키보드 기반 인터페이스다. Shell이 사용자 명령을 받아 실행하는 Command Interpreter 역할을 한다. bash, csh 등이 대표적이며, 명령은 Shell 내부에 내장된 built-in command(cd, alias 등)와 별도 프로그램으로 구현된 명령(ls, vi 등) 두 가지 방식으로 구현된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GUI(Graphic User Interface)&lt;/b&gt; 는 마우스 기반 인터페이스로 창과 메뉴 시스템으로 구성되며 아이콘이 파일, 프로그램, 동작을 나타낸다. 현대 OS(Windows, Mac OS X, Linux)는 CLI와 GUI를 모두 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. System Call&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l2_d2_syscall_flow.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKL2RE/dJMcagSSuto/yneIzK1KXU1Yf6rIpOwReK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKL2RE/dJMcagSSuto/yneIzK1KXU1Yf6rIpOwReK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKL2RE/dJMcagSSuto/yneIzK1KXU1Yf6rIpOwReK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKL2RE%2FdJMcagSSuto%2FyneIzK1KXU1Yf6rIpOwReK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1900&quot; height=&quot;1300&quot; data-filename=&quot;l2_d2_syscall_flow.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;System Call이란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System Call은 프로세스가 OS에게 서비스를 요청하는 인터페이스.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스와 OS 사이의 필수적인 연결 창구로, 일반적으로 C/C++로 작성된다. 하드 디스크 접근, 새 프로세스 생성 등이 대표적인 예다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 System Call이 필요한가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스는 User Mode에서 실행되므로 하드웨어에 직접 접근할 수 없다. 하드웨어 접근이 필요한 작업은 반드시 Kernel Mode로 전환해서 수행해야 하고, 그 전환 요청이 바로 System Call이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;API vs System Call&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머는 System Call을 직접 호출하기보다 &lt;b&gt;API(Application Program Interface)&lt;/b&gt; 를 통해 간접적으로 사용한다. 대표적인 API로는 Win32 API(Windows), POSIX API(UNIX/Linux), Java API가 있다. API를 사용하는 이유는 두 가지다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;첫째로 &lt;b&gt;Portability(이식성)&lt;/b&gt; 로, API는 OS 내부 구현과 분리되어 있어 같은 코드가 다른 OS에서도 동작한다.&lt;/li&gt;
&lt;li&gt;둘째로 &lt;b&gt;Easy to use(편의성)&lt;/b&gt; 로, 프로그래머는 System Call의 구현 방식을 알 필요 없이 API 사용법만 알면 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;System Call Handling&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System Call이 실행되면 User Program이 &lt;code&gt;fork()&lt;/code&gt; 같은 함수를 호출하고, libc의 래퍼 함수가 &lt;code&gt;%eax&lt;/code&gt; 레지스터에 시스템 콜 번호를 저장한 뒤 &lt;code&gt;int $0x80&lt;/code&gt; 명령으로 소프트웨어 인터럽트를 발생시킨다. 이 인터럽트는 Interrupt Vector Table의 &lt;code&gt;0x80&lt;/code&gt; 항목인 &lt;code&gt;system_call()&lt;/code&gt; 함수를 실행하고, System Call Table에서 번호에 해당하는 실제 커널 함수(예: &lt;code&gt;sys_fork()&lt;/code&gt;)를 찾아 실행한다. 작업이 완료되면 결과값을 User Program에 반환한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파라미터 전달 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System Call은 식별 번호 외에 추가 정보가 필요할 때가 많다. 매개변수 전달 방식은 세 가지다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;레지스터(Registers)&lt;/b&gt; 에 직접 넣는 방식은 가장 빠르지만 넣을 수 있는 양이 제한적이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;블록/테이블(Block/Table)&lt;/b&gt; 방식은 매개변수를 메모리 블록에 저장하고 그 주소만 레지스터로 전달한다(Linux, Solaris 사용).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스택(Stack)&lt;/b&gt; 방식은 프로그램의 스택에 매개변수를 push하고 커널이 pop하여 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;System Call의 종류&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;분류&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Process control&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;프로세스 생성/종료/실행&lt;/td&gt;
&lt;td&gt;fork(), execve(), getpid()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;File management&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;파일 생성/삭제/읽기/쓰기&lt;/td&gt;
&lt;td&gt;open(), read(), write(), close()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Memory management&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;메모리 할당&lt;/td&gt;
&lt;td&gt;brk()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Information maintenance&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;시간, 속성 조회/설정&lt;/td&gt;
&lt;td&gt;time()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Communications&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;프로세스 간 통신&lt;/td&gt;
&lt;td&gt;socket(), bind(), connect()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. OS 설계 원칙&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Policy와 Mechanism의 분리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS 설계에서 가장 중요한 원칙 중 하나다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Mechanism(메커니즘)&lt;/b&gt; 은 &quot;어떻게(how) 할 것인가&quot;에 대한 것이다. 예를 들어 CPU 보호를 위한 Timer 구현이 해당한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Policy(정책)&lt;/b&gt; 는 &quot;무엇을(what) 할 것인가&quot;에 대한 것이다. 예를 들어 Timer를 얼마로 설정할 것인가가 해당한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘을 분리하면 Policy가 바뀌어도 Mechanism을 바꾸지 않아도 된다. CPU Scheduling에서 Mechanism은 &quot;우선순위에 따라 프로세스를 선택하는 기능&quot;이고, Policy는 &quot;I/O bound 프로세스에게 더 높은 우선순위를 줄 것인가&quot;다. Policy만 바꿔서 다양한 스케줄링 전략을 적용할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. OS 구조&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l2_d3_os_structure.png&quot; data-origin-width=&quot;1950&quot; data-origin-height=&quot;1300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rdtQK/dJMcagZBUNk/KBobAzzDjx4PouUlE4o1l1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rdtQK/dJMcagZBUNk/KBobAzzDjx4PouUlE4o1l1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rdtQK/dJMcagZBUNk/KBobAzzDjx4PouUlE4o1l1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrdtQK%2FdJMcagZBUNk%2FKBobAzzDjx4PouUlE4o1l1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1950&quot; height=&quot;1300&quot; data-filename=&quot;l2_d3_os_structure.png&quot; data-origin-width=&quot;1950&quot; data-origin-height=&quot;1300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Simple Structure (단순 구조)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MS-DOS, 초기 UNIX가 대표적이다. 인터페이스와 기능 계층이 잘 분리되어 있지 않아 애플리케이션이 I/O 루틴에 직접 접근할 수 있었다. 하나의 애플리케이션이 실패하면 시스템 전체가 충돌할 수 있다는 심각한 문제가 있다. 단순하지만 안전하지 않고, 유지보수도 어렵다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Layered Structure (계층 구조)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS를 여러 개의 계층(Layer)으로 나누는 구조다. 최하위 계층(Layer 0)은 하드웨어, 최상위 계층(Layer N-1)은 사용자 인터페이스다. 각 계층은 그 아래 계층의 기능만 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점은 디버그가 쉽다는 점이다. 단점은 계층을 명확히 정의하기 어렵고, 계층을 거칠 때마다 오버헤드가 발생하여 비효율적이라는 점이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Microkernel Structure (마이크로커널 구조)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널에서 핵심 기능(IPC, Virtual Memory, Scheduling)만 남기고 나머지(파일 시스템, 디바이스 드라이버 등)를 사용자 공간으로 이동시킨 구조다. 사용자 모듈 간 통신은 &lt;b&gt;Message Passing&lt;/b&gt;을 통해 이루어진다. Mach, QNX, Windows NT가 대표적인 예다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점은 확장이 쉽고(easy to extend), 이식성이 좋으며(easy to port), 커널에서 실행되는 코드가 적어 안전하다(reliable and secure)는 것이다.&lt;/li&gt;
&lt;li&gt;단점은 사용자 모듈과 커널 간의 빈번한 통신으로 인한 &lt;b&gt;성능 저하(Performance Degradation)&lt;/b&gt; 다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Module Structure (모듈 구조)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 OS 대부분이 채택하는 방식이다. 객체지향 방식으로 커널을 구현하며, 각 핵심 컴포넌트를 분리된 모듈로 만든다. 각 모듈은 알려진 인터페이스로 서로 통신하며, 필요할 때만 커널에 동적으로 로드된다. Solaris, Linux, Mac OS X가 대표적이다. Microkernel과 달리 모듈들이 커널 공간에서 실행되므로 성능 저하 문제가 없고, Layered보다 훨씬 유연하다.&lt;/p&gt;
&lt;table style=&quot;height: 96px;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;구조&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;특징&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;장점&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;단점&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Simple&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;계층 없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;단순함&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;불안정, 비보안&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;MS-DOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Layered&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;계층별 분리&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;디버그 용이&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;비효율적&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Microkernel&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;최소 커널 + 사용자 공간&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;안정적, 이식성&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;성능 저하&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Mach, QNX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Module&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동적 모듈 로드&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;유연성, 성능&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;인터페이스 설계 복잡&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Linux, Solaris&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. System Boot&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;l2_d4_boot_process.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csXI6B/dJMb99TJFmC/Umx3dR4GHws4VbEALxSbDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csXI6B/dJMb99TJFmC/Umx3dR4GHws4VbEALxSbDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csXI6B/dJMb99TJFmC/Umx3dR4GHws4VbEALxSbDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsXI6B%2FdJMb99TJFmC%2FUmx3dR4GHws4VbEALxSbDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1900&quot; height=&quot;1100&quot; data-filename=&quot;l2_d4_boot_process.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;1100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Bootloader&lt;/b&gt;는 컴퓨터 전원이 켜질 때 OS를 디스크에서 메모리로 적재(load)하는 프로그램이다. 시스템 진단(diagnostics) 실행, 커널 위치 탐색, 커널을 메모리에 적재하고 시작하는 역할을 한다. 소형 시스템은 Bootloader와 OS를 ROM에 저장하고, PC 같은 대형 시스템은 Bootloader는 ROM에, OS는 디스크에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux 기반 PC의 부팅 과정은 ① BIOS 실행(ROM에서 하드웨어 진단 및 부팅 장치 탐색) &amp;rarr; ② Bootloader 실행(디스크의 boot block에서 커널을 메모리에 적재) &amp;rarr; ③ 커널 실행(하드웨어 초기화, 루트 파일시스템 마운트) &amp;rarr; ④ init 프로세스 실행(PID 1, daemon&amp;middot;shell 등 시작) 순서로 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS는 사용자를 위한 서비스(UI, 프로그램 실행, I/O, 파일 관리, 통신, 오류 감지)와 시스템 효율을 위한 서비스(자원 할당, 회계, 보호/보안)를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System Call은 프로세스가 OS에 서비스를 요청하는 인터페이스로, 프로그래머는 직접 System Call을 쓰지 않고 API(Win32, POSIX, Java)를 통해 간접적으로 사용한다. API는 이식성과 편의성을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS 구조는 Simple &amp;rarr; Layered &amp;rarr; Microkernel &amp;rarr; Module 순으로 발전했다. 현대 OS는 대부분 Module 구조를 채택하며, 마이크로커널의 안정성과 모듈의 성능을 함께 추구한다. 부팅 과정은 BIOS &amp;rarr; Bootloader &amp;rarr; Kernel &amp;rarr; init 순서로 진행된다.&lt;/p&gt;</description>
      <category>Computer Science/OS(운영 체제)</category>
      <category>CS</category>
      <category>OS</category>
      <category>운영체제</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/272</guid>
      <comments>https://wondrous-developer.tistory.com/272#entry272comment</comments>
      <pubDate>Wed, 29 Apr 2026 16:02:15 +0900</pubDate>
    </item>
    <item>
      <title>[OS] 1. Introduction to Operating Systems</title>
      <link>https://wondrous-developer.tistory.com/271</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;운영체제는 어디에 있고, 어떤 구조로 움직일까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제를 처음 배울 때 가장 먼저 헷갈리는 지점은 &amp;ldquo;운영체제가 정확히 무엇인가?&amp;rdquo;이다. 프로그램 같기도 하고, 컴퓨터 전체를 관리하는 관리자 같기도 하고, 사용자가 보는 화면 그 자체처럼 느껴지기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1강과 2강은 이 질문을 하나씩 정리해준다. 1강은 운영체제의 역할과 컴퓨터 시스템 전체 구조를 잡아주고, 2강은 그 운영체제가 사용자 프로그램에게 어떤 서비스를 제공하며 어떤 내부 구조로 구현되는지 보여준다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영체제는 하드웨어와 프로그램 사이에 있다&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;os-layer-structure.jpg&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bW4j3a/dJMb99UHp4Y/dtMdnhw3WczhxWPPBPbJKk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bW4j3a/dJMb99UHp4Y/dtMdnhw3WczhxWPPBPbJKk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bW4j3a/dJMb99UHp4Y/dtMdnhw3WczhxWPPBPbJKk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbW4j3a%2FdJMb99UHp4Y%2FdtMdnhw3WczhxWPPBPbJKk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2560&quot; height=&quot;1520&quot; data-filename=&quot;os-layer-structure.jpg&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 응용 프로그램과 하드웨어 사이에 있다. 사용자는 브라우저, 에디터, 게임 같은 응용 프로그램을 실행하지만, 그 프로그램이 CPU를 얼마나 쓸지, 메모리를 어디에 둘지, 디스크 파일을 어떻게 읽을지는 직접 결정하지 않는다. 이런 일을 대신 관리하는 소프트웨어가 운영체제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 크게 두 관점에서 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 관점에서는 컴퓨터를 편리하게 쓰게 해주는 프로그램이다. 파일을 열고, 프로그램을 실행하고, 장치를 사용할 때 하드웨어의 복잡한 세부사항을 숨겨준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 관점에서는 자원 관리자다. CPU, memory, storage, I/O device 같은 자원을 여러 프로그램에 나누어준다. 동시에 잘못된 프로그램이 다른 프로그램이나 운영체제 자체를 망가뜨리지 않도록 보호한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 운영체제의 핵심 부분을 &lt;b&gt;kernel&lt;/b&gt;이라고 한다. kernel은 컴퓨터가 켜져 있는 동안 계속 메모리에 올라와 있고, 필요한 순간에 하드웨어와 직접 상호작용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Interrupt: 운영체제가 끼어드는 순간&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 항상 모든 일을 직접 실행하고 있는 것이 아니다. 보통은 사용자 프로그램이 CPU를 사용하다가, 중요한 발생하면 운영체제가 개입한다. 이 사건을 대표하는 개념이 &lt;b&gt;interrupt&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 디스크에서 데이터를 읽는 작업이 끝났거나, 키보드 입력이 들어왔거나, timer가 울렸거나, 프로그램이 system call을 요청하면 CPU는 현재 작업을 잠시 멈추고 운영체제의 interrupt service routine으로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름은 대략 이렇다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자 프로그램 실행&lt;/li&gt;
&lt;li&gt;interrupt 또는 trap 발생&lt;/li&gt;
&lt;li&gt;CPU 상태 저장&lt;/li&gt;
&lt;li&gt;interrupt handler 실행&lt;/li&gt;
&lt;li&gt;원래 작업으로 복귀&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조 덕분에 운영체제는 필요한 순간에만 개입하면서도 시스템 전체를 통제할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;저장장치 계층과 caching&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;storage-hierarchy-pyramid.jpg&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsAVL1/dJMcafgnw7P/4WQDvuAG0wuA9gYLmIN2RK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsAVL1/dJMcafgnw7P/4WQDvuAG0wuA9gYLmIN2RK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsAVL1/dJMcafgnw7P/4WQDvuAG0wuA9gYLmIN2RK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsAVL1%2FdJMcafgnw7P%2F4WQDvuAG0wuA9gYLmIN2RK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2560&quot; height=&quot;1520&quot; data-filename=&quot;storage-hierarchy-pyramid.jpg&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터의 저장장치는 비용과 속도의 trade off가 존재한다. register와 cache는 매우 빠르지만 작고 비싸다. main memory는 그보다 느리지만 더 크다. SSD나 HDD 같은 secondary storage는 훨씬 크지만 CPU가 직접 명령어를 실행할 수 있을 만큼 빠르지는 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 컴퓨터 시스템은 저장장치를 계층 구조로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 쓰는 데이터는 빠른 곳에 두고, 덜 자주 쓰는 데이터는 큰 저장장치에 둔다. 이때 &lt;b&gt;caching&lt;/b&gt;이란 느린 저장장치의 데이터를 빠른 저장장치에 복사해두고, 다음 접근을 빠르게 만드는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 cache가 많아지면 같은 데이터의 복사본이 여러 곳에 생긴다. 이때 어느 복사본이 최신인지 맞추는 문제가 생기는데, 이를 cache coherency 문제라고 볼 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Multiprogramming과 Timesharing&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제가 여러 프로그램을 동시에 실행하는 것처럼 보이게 만드는 기본 아이디어는 multiprogramming이다. multiprogramming이란 한 프로그램이 I/O를 기다리는 동안 CPU를 놀리지 않고 다른 프로그램을 실행한하는 기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Timesharing은 여기서 한 단계 더 나아가 CPU 시간을 작은 조각으로 나누어 여러 사용자와 프로그램이 동시에 컴퓨터를 쓰는 것처럼 느끼게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 CPU가 하나라면 한 순간에 실행되는 프로그램은 하나뿐이다. 하지만 운영체제가 매우 빠르게 전환해주기 때문에 사용자는 여러 프로그램이 동시에 움직이는 것처럼 느낀다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;User Mode와 Kernel Mode&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제가 보호를 제공하려면 일반 프로그램과 kernel을 구분해야 한다. 그래서 현대 시스템은 보통 &lt;b&gt;dual-mode operation&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 프로그램은 user mode에서 실행된다. 이 모드에서는 하드웨어를 직접 제어하거나, 다른 프로그램의 메모리를 마음대로 건드릴 수 없다. 반면 kernel은 kernel mode에서 실행되며, 보호된 자원에 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘 사이를 오가는 통로가 system call이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;System Call: 사용자 프로그램이 kernel에게 부탁하는 방법&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;system-call-flow.jpg&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AoKkE/dJMcafgnw8M/bv6xK5kva1Hk0bWcy1jefk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AoKkE/dJMcafgnw8M/bv6xK5kva1Hk0bWcy1jefk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AoKkE/dJMcafgnw8M/bv6xK5kva1Hk0bWcy1jefk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAoKkE%2FdJMcafgnw8M%2Fbv6xK5kva1Hk0bWcy1jefk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2560&quot; height=&quot;1520&quot; data-filename=&quot;system-call-flow.jpg&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 프로그램은 파일을 읽거나, 프로세스를 만들거나, 네트워크를 사용하고 싶을 때 운영체제의 도움이 필요하다. 그런데 아무 코드나 kernel 내부로 직접 뛰어들 수 있으면 시스템이 위험해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 운영체제는 &lt;b&gt;system call&lt;/b&gt;이라는 정해진 입구를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 C에서 &lt;code&gt;printf()&lt;/code&gt;를 호출하면 겉으로는 화면에 문자열을 출력하는 함수처럼 보인다. 하지만 실제로는 library 내부에서 &lt;code&gt;write()&lt;/code&gt; 같은 system call을 호출해 kernel에게 출력을 요청할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;printf(&quot;hello\n&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 대략 이런 흐름으로 내려간다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;printf()
&amp;rarr; C library
&amp;rarr; write() system call
&amp;rarr; trap
&amp;rarr; kernel mode
&amp;rarr; device/file 처리
&amp;rarr; user mode 복귀&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 편한 API를 쓰고, API는 system call을 감싼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면 system call은 user mode와 kernel mode 사이를 안전하게 연결한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영체제의 내부 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 기능이 많다. 프로세스 관리, 메모리 관리, 파일 시스템, I/O 시스템, 보안, 네트워크 등 거의 모든 시스템 자원을 다룬다. 그래서 내부 구조를 어떻게 잡느냐가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 구조는 구현이 쉽지만, 모듈 경계가 약해 유지보수가 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Layered structure는 계층을 나누어 각 계층이 아래 계층 기능만 사용하게 한다. 이해와 검증은 쉬워지지만, 계층을 지날 때마다 비용이 생기고 설계가 경직될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microkernel은 kernel 안에는 최소 기능만 남기고, 파일 시스템이나 장치 드라이버 같은 기능을 user space 서버로 분리한다. 안정성과 확장성은 좋아지지만, 서버 사이 message passing 비용이 커질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Module 구조는 핵심 kernel은 유지하면서 필요한 기능을 동적으로 추가하거나 제거한다. 현대 운영체제에서 현실적인 균형점으로 자주 등장한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Boot: 운영체제가 시작되는 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터가 켜진다고 바로 운영체제가 실행되는 것은 아니다. 처음에는 firmware나 BIOS가 하드웨어를 점검하고, bootloader가 운영체제 kernel을 찾아 메모리에 올린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤 kernel이 초기화되고 root filesystem을 mount한다. 이후 init process나 daemon, shell, GUI 환경이 차례로 올라오면서 우리가 아는 컴퓨터 사용 환경이 만들어진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1~2강을 한 문장으로 묶으면 이렇게 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 응용 프로그램과 하드웨어 사이에서 자원을 관리하고, system call이라는 통제된 입구를 통해 kernel 서비스를 제공하며, 내부 구조에 따라 안정성&amp;middot;성능&amp;middot;확장성의 균형을 잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 운영체제를 &amp;ldquo;컴퓨터를 켜면 있는 무언가&amp;rdquo; 정도로 느낄 수 있다. 하지만 구조를 따라가면 조금 더 선명해진다. 운영체제는 하드웨어를 직접 다루는 위험한 일을 감싸고, 프로그램들이 서로 방해하지 않게 조율하며, 사용자가 복잡한 시스템을 편하게 쓰도록 만들어주는 중간 관리자다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영체제 Lecture 1. Introduction to Operating Systems&lt;/li&gt;
&lt;li&gt;운영체제 Lecture 2. Operating System Structures&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/OS(운영 체제)</category>
      <category>CS</category>
      <category>OS</category>
      <category>운영체제</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/271</guid>
      <comments>https://wondrous-developer.tistory.com/271#entry271comment</comments>
      <pubDate>Wed, 29 Apr 2026 11:51:09 +0900</pubDate>
    </item>
    <item>
      <title>[SW Engineering] 10. 의존성 시스템(Dependable System)</title>
      <link>https://wondrous-developer.tistory.com/269</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 매일 사용하는 뱅킹 시스템, 항공 관제 소프트웨어, 의료 기기 등은 단 한 번의 오류로도 막대한 경제적 손실이나 인명 피해를 초래할 수 있습니다. 이러한 시스템에서 가장 중요한 속성은 바로 &lt;b&gt;의존성(Dependability)&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 시스템 의존성의 핵심 요소와 이를 달성하기 위한 전략인 중복성, 다양성, 그리고 프로세스적 접근법에 대해 정리해 봅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 시스템 의존성 (System Dependability)의 이해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의존성(Dependability)&lt;/b&gt;은 사용자가 시스템을 얼마나 신뢰(Trust)할 수 있는지를 나타내는 척도입니다. 단순히 기능이 작동하는 것을 넘어, 시스템이 '실패'하지 않을 것이라는 확신을 주는 것이 핵심입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 의존성의 5대 핵심 속성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성은 다음의 5가지 상호 의존적인 속성들로 구성됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;가용성 (Availability):&lt;/b&gt; 사용자가 서비스를 요청했을 때 시스템이 즉시 응답할 수 있는 능력입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;신뢰성 (Reliability):&lt;/b&gt; 지정된 시간 동안 명세서에 기술된 대로 오류 없이 서비스를 제공하는 능력입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안전성 (Safety):&lt;/b&gt; 시스템 실패가 인명 피해나 환경 파괴 같은 치명적인 결과(Catastrophic failure)를 초래하지 않도록 하는 능력입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안성 (Security):&lt;/b&gt; 외부의 악의적인 공격으로부터 시스템과 데이터를 보호하는 능력입니다. 보안 실패는 가용성과 신뢰성 저하로 직결됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;회복력 (Resilience):&lt;/b&gt; 예기치 못한 장애가 발생해도 서비스 중단을 최소화하고 빠르게 정상 상태로 회복하는 능력입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;의존성 시스템 설계가 어려운 이유는 이 5가지 속성이 서로 &lt;b data-index-in-node=&quot;34&quot; data-path-to-node=&quot;21,0&quot;&gt;트레이드오프(Trade-off)&lt;/b&gt; 관계에 있거나 긴밀하게 연결되어 있기 때문입니다. 특히 현대 소프트웨어에서 &lt;b&gt;보안(Security)&lt;/b&gt;은 신뢰성, 가용성, 안전성 모두를 무너뜨릴 수 있는 가장 치명적인 시작점이 되곤 합니다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사회에서 흔히 일어나는 몇가지 예시를 살펴볼까요?&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오 1: 보안성이 신뢰성에 영향을 미치는 경우&lt;/blockquote&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;보안성이 무너지면 시스템이 '명세서대로 정확하게' 작동할 수 없으므로 신뢰성까지 함께 무너지게 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;상황:&lt;/b&gt; 온라인 뱅킹 시스템의 데이터베이스가 해킹당해 잔액 정보가 위조되었습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;보안성 실패:&lt;/b&gt; 해커가 권한 없이 시스템에 침투하여 데이터를 조작했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;신뢰성으로의 전이:&lt;/b&gt; 은행 전산망(하드웨어)은 멀쩡히 돌아가고 있지만, 사용자가 잔액 조회를 했을 때 실제와 다른 '잘못된 정보'를 보여주게 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0&quot;&gt;결과:&lt;/b&gt; 시스템이 명세대로(정확한 잔액 표시) 서비스를 제공하지 못하게 되었으므로, 사용자는 더 이상 이 시스템을 &lt;b&gt;신뢰(Reliability)&lt;/b&gt;할 수 없게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오 2: 가용성이 신뢰성에 영향을 미치는 경우&lt;/blockquote&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;가용성 실패가 신뢰성 저하로 이어질 수 있으며 때로는 신뢰성을 위해 일부러 가용성을 낮추기도 합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;①&lt;/span&gt; 가용성 실패가 신뢰성 저하로 이어지는 경우&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;가용성이 낮아지면(자주 끊기면), 시스템이 명세대로 작동하더라도 사용자는 그 결과를 믿지 못하게 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;예시:&lt;/b&gt; 실시간 주식 거래 시스템&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;상황:&lt;/b&gt; 시스템이 과부하로 인해 1분간 멈췄습니다(&lt;b data-index-in-node=&quot;27&quot; data-path-to-node=&quot;6,1,0&quot;&gt;가용성 상실&lt;/b&gt;). 그 사이 주가는 급변했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,2,0&quot;&gt;영향:&lt;/b&gt; 다시 시스템이 복구되어 현재 주가를 정확히 표시하더라도(&lt;b data-index-in-node=&quot;35&quot; data-path-to-node=&quot;6,2,0&quot;&gt;신뢰성 회복&lt;/b&gt;), 사용자는 &quot;아까 멈췄을 때 내가 판 주문이 제대로 체결됐을까?&quot; 혹은 &quot;지금 데이터가 정말 실시간이 맞나?&quot;라고 의심하게 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,3,0&quot;&gt;결론:&lt;/b&gt; 잦은 가동 중단(Low Availability)은 시스템이 제공하는 정보의 정확도(Reliability)에 대한 사용자의 신뢰를 근본적으로 훼손합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;② 신뢰성 확보를 위해 가용성을 의도적으로 낮추는 경우&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;때로는 '잘못된 서비스'를 제공하느니 차라리 '서비스를 중단'하는 것이 전체적인 의존성을 높이는 길일 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;예시:&lt;/b&gt; 은행 점검 시간 (Batch Processing)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;상황:&lt;/b&gt; 은행이 데이터의 무결성과 정확성(&lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;9,1,0&quot;&gt;신뢰성&lt;/b&gt;)을 100% 보장하기 위해, 매일 새벽 대규모 데이터 동기화 작업을 수행합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;영향:&lt;/b&gt; 이 작업 동안은 인터넷 뱅킹 접속이 차단됩니다(&lt;b data-index-in-node=&quot;30&quot; data-path-to-node=&quot;9,2,0&quot;&gt;가용성 저하&lt;/b&gt;).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,3,0&quot;&gt;결론:&lt;/b&gt; 시스템이 오류 없이 완벽하게 작동한다는 확신(High Reliability)을 주기 위해, 특정 시간 동안 서비스 응답 능력(Availability)을 포기하는 전략적 선택입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 보조 속성 (Secondary Properties)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;수리 용이성 (Repairability):&lt;/b&gt; 장애 발생 시 얼마나 빠르고 쉽게 복구할 수 있는가.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유지보수성 (Maintainability):&lt;/b&gt; 새로운 비즈니스 요구사항에 맞춰 시스템을 얼마나 쉽게 수정할 수 있는가.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오류 허용성 (Error Tolerance):&lt;/b&gt; 사용자의 실수나 잘못된 입력에도 시스템이 견디고 올바르게 반응하는가.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 의존성 확보와 비용의 관계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템의 의존성 수준을 높일수록 이를 구현하기 위한 비용은 &lt;b&gt;기하급수적(Exponentially)&lt;/b&gt;으로 증가합니다. 따라서 무조건적인 완벽함보다는 서비스의 목적과 경제적 타당성을 고려한 &lt;b&gt;적정 의존성 수준&lt;/b&gt;을 설정하는 것이 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 사회-기술적 시스템 (Sociotechnical Systems)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 소프트웨어는 독립적으로 존재하지 않습니다. 하드웨어, 인간, 조직, 사회라는 거대한 계층 구조의 일부로 작동합니다. 이를 &lt;b&gt;사회-기술적 시스템&lt;/b&gt;이라고 부릅니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시스템 계층 구조 (System Layers)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류는 한 계층에서 발생하여 다른 계층으로 전파(Propagation)될 수 있으므로 전체론적 관점이 필요합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;사회 (Society):&lt;/b&gt; 법률, 규제 체계, 사회적 문화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조직 (Organization):&lt;/b&gt; 비즈니스 전략, 기업 정책&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비즈니스 프로세스 (Business Processes):&lt;/b&gt; 업무 수행 절차&lt;/li&gt;
&lt;li&gt;&lt;b&gt;애플리케이션 시스템 (Application System):&lt;/b&gt; 실제 소프트웨어 로직&lt;/li&gt;
&lt;li&gt;&lt;b&gt;통신 및 데이터 (Communications and Data):&lt;/b&gt; 미들웨어, 네트워크 기술&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영체제 및 장비 (OS &amp;amp; Equipment):&lt;/b&gt; 커널, 서버 하드웨어&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 항공이나 원자력 같은 &lt;b&gt;중요 시스템(Critical Systems)&lt;/b&gt;은 국가 기관의 &lt;b&gt;승인(Certification)&lt;/b&gt;이 필수이며, 이를 위해 규정 준수를 증명하는 방대한 문서(Safety Case) 작성이 수반됩니다. 또한&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;소프트웨어 공학에서 시스템을 설계할 때는 코드 그 자체만 보는 것이 아니라, 그 소프트웨어가 속한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b data-path-to-node=&quot;27&quot; data-index-in-node=&quot;59&quot;&gt;사회적 맥락과 조직의 요구사항이 기술에 어떤 영향을 미치는지&lt;/b&gt;를 아우르는 '전체론적 관점'이 반드시 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 현실적인 예시를 통해 살펴볼까요? 다음 두 가지 시나리오는 각각 하위 계층의 문제가 상위 계층으로 전파되는 예시와 그 반대의 예시입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오 1: 쿠팡 해킹과 시스템 계층 간 영향&lt;/blockquote&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;최근 SKT 정보유출을 화두로 지속적으로 고객 정보 해킹 피해가 증가하고 있습니다. 최근의 쿠팡의 개인정보 유출 이슈를 바탕으로&amp;nbsp; &lt;b&gt;사회-기술적 시스템(Sociotechnical Systems)&lt;/b&gt;의 계층 구조 관점에서 살펴보면 기술적 오류가 어떻게 조직과 사회 전체로 전파되는지 명확히 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt; ① &lt;/span&gt;운영체제 및 장비 / 통신 및 데이터 계층 (하위 기술 계층)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;영향:&lt;/b&gt; 서버의 과부하나 네트워크 설정 오류, 혹은 데이터베이스(DB) 서버의 일시적인 동기화 오류가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;상황:&lt;/b&gt; 특정 업데이트 직후 서버 패킷 처리 과정에서 데이터 매칭이 어긋나는 기술적 결함이 발생하며 유출의 시발점이 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;② &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;애플리케이션 시스템 계층 (소프트웨어 로직)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;영향:&lt;/b&gt; 실제 '내 정보 보기'나 '주문 내역' 화면에서 본인의 ID가 아닌 다른 사용자의 데이터를 불러오는 &lt;b data-index-in-node=&quot;60&quot; data-path-to-node=&quot;6,0,0&quot;&gt;로직 결함&lt;/b&gt;이 발생합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;상황:&lt;/b&gt; 보안성(Security)의 핵심인 권한 검증 로직이 실패하면서, 시스템의 **신뢰성(Reliability)**이 무너지고 실제 개인정보가 화면에 노출됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;③ 비즈니스 프로세스 계층 (업무 절차)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;영향:&lt;/b&gt; 오류 발생 시 즉각적인 서비스 중단이나 모니터링 절차가 미흡할 경우 피해가 확산됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;상황:&lt;/b&gt; 배포 전 검증(V&amp;amp;V) 절차에서 해당 오류를 잡아내지 못했거나, 유출 신고 접수 후 시스템 차단까지의 대응 프로세스가 지연되면서 더 많은 사용자가 피해를 입게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;④ 조직 계층 (비즈니스 전략 및 정책)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;영향:&lt;/b&gt; 보안보다 빠른 배포와 성과를 우선시하는 조직 문화나, 개인정보 보호 정책의 부재가 근본적인 원인이 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;상황:&lt;/b&gt; 쿠팡이라는 기업의 대외 이미지와 브랜드 가치가 하락하며, 고객들의 탈퇴(Coupang Exit)로 이어져 경영 전략에 차질을 빚게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;⑤&lt;/span&gt; 사회 계층 (법률, 규제 및 문화)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;영향:&lt;/b&gt; 유출 사고는 단순히 한 기업의 문제를 넘어 국가적 규제와 법적 책임으로 확장됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;상황:&lt;/b&gt; 개인정보보호위원회 등 규제 기관의 조사와 막대한 과징금 부과가 이루어집니다. 또한, 사회적으로 &quot;대형 플랫폼도 믿을 수 없다&quot;는 불신이 확산되어 개인정보 보호에 대한 법적 규제가 강화되는 계기가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;따라서 이 사건은 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;14&quot;&gt;애플리케이션 계층&lt;/b&gt;의 작은 소프트웨어 버그(전파의 시작)가 &lt;b data-index-in-node=&quot;48&quot; data-path-to-node=&quot;14&quot;&gt;비즈니스 프로세스&lt;/b&gt;의 대응 미흡을 타고 흘러가, 결국 &lt;b data-index-in-node=&quot;77&quot; data-path-to-node=&quot;14&quot;&gt;조직&lt;/b&gt;의 경제적 손실과 &lt;b data-index-in-node=&quot;89&quot; data-path-to-node=&quot;14&quot;&gt;사회&lt;/b&gt;적 규제 강화라는 최상위 계층까지 영향을 미친 &lt;b data-index-in-node=&quot;117&quot; data-path-to-node=&quot;14&quot;&gt;전형적인 사회-기술적 시스템의 실패 사례&lt;/b&gt;라고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;때로는 반대로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b data-path-to-node=&quot;4&quot; data-index-in-node=&quot;30&quot;&gt;세상의 변화와 사회적 요구가 거꾸로 기술의 형태를 결정&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;하기도 합니다.&lt;span&gt; 다음 사례를 통해 어떻게 사회 문제가 어떻게 앱 화면의 인터페이스와 로직까지 바꾸게 되는지 살펴봅시다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-path-to-node=&quot;14&quot; data-ke-style=&quot;style3&quot;&gt;시나리오 2: 공유 킥보드의 '무단 주차' 문제&lt;/blockquote&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;① &lt;/span&gt;&lt;b&gt;Society&lt;/b&gt; 계층&lt;b&gt;: 문제의 발생&lt;/b&gt; 사용자들이 킥보드를 보도 한가운데나 상점 입구에 방치하면서 보행자의 통행을 방해합니다. 이는 곧 대중의 분노와 민원으로 이어지며, &quot;공유 킥보드는 사회적 흉물&quot;이라는 강력한 부정적 여론이 형성됩니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;②&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; &lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20&quot;&gt;Society &amp;rarr; Organization: 규제 압박과 경영 위기&lt;/b&gt; 부정적 여론이 거세지면 지자체는 견인 조치나 과태료 부과 등 강력한 규제를 도입합니다. 운영사(조직)는 이미지 실추와 견인 비용 증가라는 타격을 입게 되며, 생존을 위해 &lt;b data-index-in-node=&quot;135&quot; data-path-to-node=&quot;20&quot;&gt;&quot;자유로운 반납&quot; 대신 &quot;지정 구역 반납&quot;으로 경영 정책을 긴급히 수정&lt;/b&gt;하게 됩니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;③ &lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21&quot;&gt;Organization &amp;rarr; Business Process: 운영 프로세스의 대전환&lt;/b&gt; 경영진의 정책 변화에 따라 내부 운영 방식이 바뀝니다. 단순히 킥보드를 배치하는 것을 넘어, &lt;b data-index-in-node=&quot;101&quot; data-path-to-node=&quot;21&quot;&gt;&quot;사용자가 지정된 장소에 주차했는가&quot;를 확인하고 검증하는 새로운 업무 절차&lt;/b&gt;가 비즈니스 프로세스에 추가됩니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;④ &lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;22&quot;&gt;Business Process &amp;rarr; Application System: 기술적 구현 (최종 단계)&lt;/b&gt; 변경된 비즈니스 프로세스를 실현하기 위해, 개발팀은 애플리케이션 시스템의 코드를 수정합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;23&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,0,0&quot;&gt;인터페이스 변경:&lt;/b&gt; 앱 지도 위에 주차 가능 구역(P)과 금지 구역(Red Zone)을 시각적으로 표시합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,1,0&quot;&gt;시스템 로직 수정:&lt;/b&gt; GPS 값을 실시간으로 대조하여, 주차 구역 내에 있을 때만 '반납 완료' 버튼이 활성화되도록 로직을 변경(Geofencing)합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,2,0&quot;&gt;기능 추가:&lt;/b&gt; 주차 상태를 증명하기 위해 반납 시 반드시 사진을 촬영하여 업로드하도록 카메라 기능을 추가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&amp;nbsp;공유 킥보드의 주차 인증 사진 기능이나 주차 구역 안내 지도는 개발자가 어느 날 갑자기 아이디어를 내서 만든 것이 아닙니다. &lt;b data-index-in-node=&quot;70&quot; data-path-to-node=&quot;26&quot;&gt;사회 계층의 갈등(무단 주차) &amp;rarr; 조직의 대응(정책 변화) &amp;rarr; 프로세스의 수정 &amp;rarr; 기술적 구현&lt;/b&gt;이라는 흐름을 거쳐 탄생한 결과물입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;29&quot; data-ke-size=&quot;size16&quot;&gt;거리의 무단 주차라는 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;29&quot;&gt;사회적 문제&lt;/b&gt;는 기업의 정책을 바꾸고, 비즈니스 절차를 수정하게 만들며, 최종적으로는 앱의 &lt;b data-index-in-node=&quot;66&quot; data-path-to-node=&quot;29&quot;&gt;기술적 기능&lt;/b&gt;까지 변화시키는 강력한 전파 효과를 가집니다. 이것이 바로 우리가 소프트웨어를 '사회-기술적 시스템'으로 이해해야 하는 이유입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 중복성과 다양성 (Redundancy and Diversity)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템의 가용성을 높이고 &lt;b&gt;단일 실패 지점(Single Point of Failure)&lt;/b&gt;을 제거하기 위한 가장 강력한 기술적 전략입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 중복성 (Redundancy)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; 핵심 컴포넌트를 예비(Backup)용으로 하나 이상 더 배치하는 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작동 원리:&lt;/b&gt; 메인 시스템에 문제가 생기면 즉시 예비 시스템이 가동되어 서비스 연속성을 보장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사례:&lt;/b&gt; 서버 이중화, 데이터 백업 서버 구축 등.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 다양성 (Diversity)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; 동일한 기능을 수행하되, 서로 다른 설계나 기술을 사용하여 구현하는 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작동 원리:&lt;/b&gt; &lt;b&gt;공통 모드 실패(Common-mode failure)&lt;/b&gt;를 방지합니다. 예를 들어, 모든 서버가 동일한 OS를 사용한다면 특정 OS 취약점에 의해 동시에 마비될 수 있지만, 서로 다른 OS를 사용하면 하나의 공격으로 전체 시스템이 무너지는 것을 막을 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사례:&lt;/b&gt; 서로 다른 제조사의 센서 사용, 다른 프로그래밍 언어로 개발된 다중 버전 소프트웨어.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이중화와 다양성에 대해 최근에 이슈가 된 국가 전산망 화재의 예시를 통해 살펴볼까요?&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오: 국가 전산망 화재를 통해 들여다 본 중복성과 다양성&lt;/blockquote&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;중복성는 시스템의 &lt;b&gt;가용성(Availability)&lt;/b&gt;을 높이기 위해 핵심 컴포넌트의 여분(Backup)을 마련하는 전략입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;화재 상황 예시:&lt;/b&gt; 서울에 있는 데이터센터에 우리 회사의 메인 서버가 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,1,0,0&quot;&gt;이중화 적용:&lt;/b&gt; 부산에 있는 데이터센터에 서울과 &lt;b data-index-in-node=&quot;26&quot; data-path-to-node=&quot;5,0,1,0,0&quot;&gt;똑같은 사양의 복제 서버&lt;/b&gt;를 한 대 더 설치합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,1,1,0&quot;&gt;결과:&lt;/b&gt; 서울 데이터센터에 화재가 발생해 서버가 타버리더라도, 부산에 있는 예비 서버가 즉시 가동되어 서비스가 중단되지 않고 계속 유지됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;다양화는 &lt;b&gt;공통 모드 실패(Common-mode failure&lt;/b&gt;)를 방지하기 위한 전략입니다. 똑같은 장비만 여러 개 두면, 특정 사고 시 모든 장비가 동시에 고장 날 수 있기 때문입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;화재 상황 예시:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터센터의 전원 공급 장치를 이중화했지만, 모두 **'A 전력사'**의 라인만 사용하고 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,1,1,0&quot;&gt;문제 발생:&lt;/b&gt; 전력사 A의 변전소에 불이 나면, 이중화된 두 라인이 동시에 끊겨 서버가 모두 꺼집니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,1,2,0&quot;&gt;다양화 적용:&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;하나는 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;9,0,1,2,0&quot;&gt;'A 전력사'&lt;/b&gt;, 다른 하나는 **'B 전력사'**로부터 전기를 공급받습니다. (전력망의 다양화)&lt;/li&gt;
&lt;li&gt;서버의 운영체제(OS)도 하나는 &lt;b data-index-in-node=&quot;92&quot; data-path-to-node=&quot;9,0,1,2,0&quot;&gt;Windows&lt;/b&gt;, 하나는 &lt;b data-index-in-node=&quot;105&quot; data-path-to-node=&quot;9,0,1,2,0&quot;&gt;Linux&lt;/b&gt;를 사용합니다. (소프트웨어의 다양화)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,1,3,0&quot;&gt;결과:&lt;/b&gt; 특정 전력망에 문제가 생기거나 특정 OS의 보안 취약점을 노린 공격이 들어와도, 다른 방식의 시스템은 영향을 받지 않고 살아남습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 의존성 보장을 위한 개발 프로세스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고도의 의존성이 요구되는 시스템을 개발할 때는 일반적인 소프트웨어 개발보다 훨씬 엄격한 프로세스가 적용됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 프로세스 특징&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;명시적 정의 (Explicitly Defined):&lt;/b&gt; 모든 활동이 문서화되어 있고 표준화되어 있어야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반복 가능성 (Repeatable):&lt;/b&gt; 특정 개발자의 역량에 의존하지 않고, 정해진 절차를 따르면 일관된 품질이 보장되어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 주요 활동&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정형 명세 (Formal Specification):&lt;/b&gt; 수학적 모델링을 통해 요구사항의 논리적 모순을 사전에 제거합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정적 분석 (Static Analysis):&lt;/b&gt; 코드를 실행하지 않고 자동화 도구를 통해 잠재적인 런타임 에러나 취약점을 탐지합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로그램 인스펙션 (Inspection):&lt;/b&gt; 동료 검토(Peer review)를 통해 설계와 코드의 결함을 다각도에서 검증합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 애자일(Agile) 방식과의 관계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성 시스템은 규제 승인을 위해 방대한 문서화와 철저한 사전 분석(Up-front Analysis)이 필수입니다. 이는 빠른 변화와 코드 중심의 애자일 철학과는 다소 상충되는 면이 있습니다. 따라서 안전 최우선 시스템에서는 순수 애자일보다는 엄격한 문서 관리가 결합된 하이브리드 프로세스를 주로 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성은 단순히 '버그 없는 코드'를 의미하지 않습니다. 시스템이 사회적, 조직적 맥락 안에서 실패 없이 가치를 제공하도록 설계하는 거시적인 설계 철학입니다. 보안이 신뢰성에 영향을 주고, 중복성이 가용성을 뒷받침하는 이러한 상호 관계를 이해하는 것이 안정적인 시스템 구축의 첫걸음입니다.&lt;/p&gt;</description>
      <category>Computer Science/SW Engineering</category>
      <category>SW Engineering</category>
      <category>SW 공학</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/269</guid>
      <comments>https://wondrous-developer.tistory.com/269#entry269comment</comments>
      <pubDate>Wed, 17 Dec 2025 20:46:04 +0900</pubDate>
    </item>
    <item>
      <title>[SW Engineering] 09. 유지보수와 레거시 전략(Software Evolution)</title>
      <link>https://wondrous-developer.tistory.com/268</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 개발이 끝났다고 프로젝트가 끝난 것이 아닙니다. 오히려 &lt;b&gt;배포 후(Post-delivery)&lt;/b&gt;가 진정한 시작입니다. 비즈니스 환경은 끊임없이 변하고, 그에 맞춰 소프트웨어도 계속 진화해야 하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 소프트웨어 진화의 중요성, &lt;b&gt;레거시 시스템 관리 전략&lt;/b&gt;, 그리고 코드 품질을 위한 &lt;b&gt;리팩터링&lt;/b&gt;에 대해 다루어 보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 소프트웨어 진화의 중요성 (Importance of Evolution)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 변화는 피할 수 없다 (Inevitable)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어는 한번 만들어지면 끝이 아니라, 계속해서 변해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 요구사항이 생겨나고, 비즈니스 환경이 변하며, 에러를 수정해야 하고, 장비가 업그레이드되기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 시스템의 분류 (Lehman's Law)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;S-타입 시스템 (Static-type):&lt;/b&gt; 스펙이 한번 정해지면 절대 변하지 않는 시스템입니다. (현실 세계에 거의 존재하지 않음).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;E-타입 시스템 (Embedded-type):&lt;/b&gt; 실제 세계 환경에 포함되어 있어, 환경이 변함에 따라 지속적으로 진화해야 하는 시스템입니다. (대부분의 소프트웨어가 여기에 해당).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 유지보수 비용의 현실&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분의 소프트웨어 예산은 새로운 개발보다 &lt;b&gt;기존 시스템을 진화(유지보수)시키는 데&lt;/b&gt; 사용됩니다.&lt;/li&gt;
&lt;li&gt;개발자 시간의 &lt;b&gt;35%~50%&lt;/b&gt;가 디버깅과 유지보수에 쓰입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 진화 프로세스 (Evolution Processes)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 수명 주기는 크게 3단계로 구분됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 진화 단계 (Evolution)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템이 운영 중이며, 새로운 요구사항에 맞춰 &lt;b&gt;기능 추가&lt;/b&gt;와 변경이 활발히 일어나는 단계입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 서비스 단계 (Servicing)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더 이상 대규모 기능 추가는 하지 않습니다. 시스템이 계속 작동하도록 &lt;b&gt;버그 수정&lt;/b&gt;이나 환경 변화(OS 패치 등)에 대한 대응만 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 폐기 단계 (Phase-out)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템이 더 이상 유용하지 않아 사용을 중단하거나 교체하는 단계입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 레거시 시스템 (Legacy Systems)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;오래된 기술로 만들어졌지만, 비즈니스에 너무 중요해서 버리지 못하고 계속 쓰는 시스템&quot;&lt;/b&gt;을 말합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 레거시 시스템의 구성 요소&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 코드만 낡은 것이 아니라, 모든 것이 얽혀 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시스템 하드웨어:&lt;/b&gt; 더 이상 생산되지 않는 구형 장비 (메인프레임 등).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지원 소프트웨어:&lt;/b&gt; 업데이트가 끊긴 구버전 OS나 컴파일러.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;응용 소프트웨어:&lt;/b&gt; 비즈니스 로직을 담고 있는 낡은 프로그램.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;응용 데이터:&lt;/b&gt; 오랫동안 쌓여 일관성이 없고 중복된 데이터.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비즈니스 프로세스:&lt;/b&gt; 시스템의 제약 때문에 굳어진 비효율적인 업무 방식.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 레거시 시스템 평가 및 전략 (Assessment Strategy)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템의 &lt;b&gt;비즈니스 가치(Business Value)&lt;/b&gt;와 &lt;b&gt;시스템 품질(System Quality)&lt;/b&gt;을 평가하여 4분면 매트릭스로 전략을 결정합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;width: 135px;&quot; align=&quot;center&quot;&gt;비즈니스 가치&lt;br /&gt;(Business Value)&lt;/th&gt;
&lt;th style=&quot;width: 145px;&quot; align=&quot;center&quot;&gt;시스템 품질&lt;br /&gt;(System Quality)&lt;/th&gt;
&lt;th style=&quot;width: 131px;&quot; align=&quot;center&quot;&gt;전략 (Strategy)&lt;/th&gt;
&lt;th style=&quot;width: 442px;&quot; align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 135px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;낮음 (Low)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 145px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;낮음 (Low)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 131px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;폐기 (Scrap)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 442px;&quot; align=&quot;left&quot;&gt;쓸모도 없고 품질도 나쁘므로 완전히 폐기합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 135px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;높음 (High)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 145px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;낮음 (Low)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 131px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;개조/재공학&lt;br /&gt;(Transform/Re-engineer)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 442px;&quot; align=&quot;left&quot;&gt;중요하지만 품질이 낮으므로, 재공학을 통해 품질을 개선하거나 교체합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 135px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;낮음 (Low)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 145px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;높음 (High)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 131px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;교체/유지&lt;br /&gt;(Replace/Maintain)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 442px;&quot; align=&quot;left&quot;&gt;품질은 좋지만 가치가 낮으므로, COTS(상용 제품)로 교체하거나 현상 유지합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 135px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;높음 (High)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 145px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;높음 (High)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 131px;&quot; align=&quot;center&quot;&gt;&lt;b&gt;유지보수 (Maintain)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 442px;&quot; align=&quot;left&quot;&gt;가장 이상적인 상태. 현재 상태로 계속 유지보수하며 사용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 시나리오를 통해 각 상황에서 어떤 전략을 사용할지 살펴봅시다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오 1: 15년 된 시스템, 개발사 지원 종료. 인사/급여 등 필수 업무 담당(High Value). 하지만 사용하기 어렵고 고장이 잦음(Low Quality).&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비즈니스 가치는 높으나 시스템 품질이 낮음 (High Value, Low Quality). 따라서 이 경우에는 교체(Replace) 전략을 사용해야 합니다. 왜냐하면 유지보수 비용이 매우 높을 것으로 예상됩니다. 원래는 재공학(Re-engineering)도 고려할 수 있으나, &lt;b&gt;개발사 지원이 종료&lt;/b&gt;되었으므로 리스크를 감수하고 새로운 시스템으로 완전히 교체(Replace)하는 것이 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오2: 10년 이상 운영, 고객 증가로 사업 성장 중(High Value). 핵심 개발 인력이 회사에 남아있어 시스템 이해도가 높음(High Maintainability/Quality). 모바일 등 신규 요구사항 증가.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이 시나리오에서는 비즈니스 가치도 높고, 핵심 인력이 있어 유지보수 품질도 확보됨. 따라서 이 경우에는 &lt;/span&gt;&lt;b&gt;진화 및 리팩터링 (Evolution with Refactoring) &lt;/b&gt;전략이 효율적입니다. 왜냐하면 시스템이 비즈니스에 기여하는 바가 크고 유지보수가 가능하므로, 전면 교체(Replace)의 리스크를 짊어질 필요가 없습니다. 현재 시스템을 유지(Maintain)하면서, 모바일 지원 등 변화하는 요구사항에 맞춰 아키텍처를 개선(Refactoring)하는 것이 최선입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 소프트웨어 유지보수 (Software Maintenance)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 유지보수의 정의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어가 인도(Delivery)된 이후에 발생하는 모든 변경 활동입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 유지보수의 유형 및 비용 비중&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기능 추가/수정 (Functionality Addition):&lt;/b&gt; 새로운 요구사항 반영. &lt;b&gt;(58% - 비용이 제일 큼!)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결함 수정 (Fault Repair):&lt;/b&gt; 버그 수정. (24%)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;환경 적응 (Environmental Adaptation):&lt;/b&gt; OS나 하드웨어 변경 대응. (19%)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 재공학 vs 리팩터링 (Re-engineering vs Refactoring)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 시스템을 개선하는 것이지만, &lt;b&gt;목적과 시기&lt;/b&gt;가 다릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;재공학:&lt;/b&gt; 유지보수 비용이 한계에 다다랐을 때 수행하는 대규모 프로젝트입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리팩터링:&lt;/b&gt; 개발과 진화 과정에서 수시로, 지속적으로 수행하는 일상적인 활동입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 리팩터링: 코드의 악취 (Bad Smells)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리팩터링이 필요한 신호를 &lt;b&gt;'악취(Bad Smells)'&lt;/b&gt;라고 부릅니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;중복 코드 (Duplicate Code):&lt;/b&gt; 똑같은 코드가 여러 곳에 복사되어 있음. &amp;rarr; 메서드로 추출하여 제거.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;긴 메서드 (Long Methods):&lt;/b&gt; 함수 하나가 너무 길고 많은 일을 함. &amp;rarr; 여러 개의 작은 메서드로 분리.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Switch 문 (Switch Statements):&lt;/b&gt; 타입에 따라 분기하는 코드가 여기저기 흩어져 있음. &amp;rarr; 객체지향의 &lt;b&gt;다형성(Polymorphism)&lt;/b&gt;으로 해결.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 뭉치 (Data Clumping):&lt;/b&gt; &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;age&lt;/code&gt;, &lt;code&gt;phone&lt;/code&gt; 처럼 항상 같이 다니는 데이터들. &amp;rarr; 하나의 &lt;b&gt;객체(Class)&lt;/b&gt;로 묶음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;추측성 일반화 (Speculative Generality):&lt;/b&gt; &quot;나중에 필요할지도 몰라&quot;라며 미리 복잡하게 만든 구조. &amp;rarr; 과감히 삭제 (YAGNI 원칙).&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어는 &lt;b&gt;S-타입&lt;/b&gt;이 아니라 &lt;b&gt;E-타입&lt;/b&gt;이므로 끊임없이 진화해야 합니다. 특히 &lt;b&gt;레거시 시스템&lt;/b&gt;은 &lt;b&gt;'가치 vs 품질'&lt;/b&gt; 매트릭스를 통해 교체할지 유지할지 냉철하게 판단해야 합니다. 개발 과정에서는 &lt;b&gt;코드의 악취&lt;/b&gt;를 지속적인 &lt;b&gt;리팩터링&lt;/b&gt;으로 제거해야 건강한 시스템을 유지할 수 있습니다.&lt;/p&gt;</description>
      <category>Computer Science/SW Engineering</category>
      <category>SW Engineering</category>
      <category>SW 공학</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/268</guid>
      <comments>https://wondrous-developer.tistory.com/268#entry268comment</comments>
      <pubDate>Wed, 17 Dec 2025 13:48:49 +0900</pubDate>
    </item>
    <item>
      <title>[SW Engineering] 08. 소프트웨어 테스팅 (Software Testing)</title>
      <link>https://wondrous-developer.tistory.com/267</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 코드를 다 짰다고 끝난 게 아닙니다. 사실 진짜 싸움은 지금부터죠. 바로 &lt;b&gt;'테스팅(Testing)'&lt;/b&gt;입니다. 이번 포스팅에서는 소프트웨어 테스팅의 목적과 단계, 그리고 요즘 개발자들의 필수 덕목인 &lt;b&gt;TDD(테스트 주도 개발)&lt;/b&gt;까지 확실하게 정리해 드립니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 프로그램 테스팅이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스팅은 프로그램이 &lt;b&gt;의도한 대로 동작하는지 확인&lt;/b&gt;하고, 사용하기 전에 &lt;b&gt;결함을 발견&lt;/b&gt;하기 위해 수행하는 활동입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 테스팅의 핵심&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;목적:&lt;/b&gt; 단순히 &quot;잘 돌아간다&quot;를 보여주는 것뿐만 아니라, &lt;b&gt;&quot;어디가 고장 났는지&quot;&lt;/b&gt; 찾아내기 위함입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;한계:&lt;/b&gt; 테스팅은 에러가 &lt;b&gt;존재함(Presence)&lt;/b&gt;을 증명할 수는 있지만, 에러가 &lt;b&gt;없음(Absence)&lt;/b&gt;을 증명할 수는 없습니다. (아무리 테스트해도 우리가 못 찾은 버그는 있을 수 있다는 뜻!)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;V&amp;amp;V 프로세스:&lt;/b&gt; 테스팅은 더 큰 개념인 &lt;b&gt;검증 및 확인(Verification &amp;amp; Validation)&lt;/b&gt; 프로세스의 일부입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 테스팅의 두 가지 목표&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;유효성 만족 증명 (Validation Testing)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;고객이 달라고 한 기능이 다 있나?&quot;&lt;/li&gt;
&lt;li&gt;개발자와 고객에게 소프트웨어가 요구사항을 충족함을 보여줍니다.&lt;/li&gt;
&lt;li&gt;성공적인 테스트 = 시스템이 &lt;b&gt;의도한 대로 작동함&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결함 발견 (Defect Testing)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;혹시 이상한 짓 하면 뻗어버리나?&quot;&lt;/li&gt;
&lt;li&gt;소프트웨어의 동작이 올바르지 않거나 명세와 다른 상황을 찾아냅니다.&lt;/li&gt;
&lt;li&gt;성공적인 테스트 = 시스템이 &lt;b&gt;고장 나게 만듦&lt;/b&gt; (버그를 찾았으니까 성공!)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 최근 검증(Verification)과 확인(Validation)이 지켜지지 않아 문제가 된 &quot;K사 ID 노출 이슈&quot;를 살펴볼까요?'&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;문제 시나리오: K사는 오픈채팅방의 익명성을 보장한다고 했으나, 기술적 허점(임시 ID 생성 방식)으로 인해 해커가 실제 사용자 정보를 추적할 수 있었습니다. K사는 &quot;설계된 대로 구현했으므로 기술적 오류는 없다&quot;고 주장하지만, 사용자는 &quot;익명성이 깨졌다&quot;고 반발하는 상황&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.catchsecu.com/archives/22374&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.catchsecu.com/archives/22374&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1765937264682&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;2024년 대기업 개인정보 문제&amp;hellip;카카오톡, 카카오페이 개인정보 총정리 | 캐치시큐&quot; data-og-description=&quot;안녕하세요. 캐치시큐입니다. 카카오의 개인정보 문제가 가장 큰 이슈였는데요. 카카오페이의 동의 없는 개인정보 제공과 카카오톡의 개인정보 유출 사건입니다. 카카오톡의 경우 국내 기업 중&quot; data-og-host=&quot;www.catchsecu.com&quot; data-og-source-url=&quot;https://www.catchsecu.com/archives/22374&quot; data-og-url=&quot;https://www.catchsecu.com/archives/22374&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cxxQF4/hyZPBsGcpI/FL9dLC3OoxCU5AGzOih481/img.png?width=486&amp;amp;height=344&amp;amp;face=0_0_486_344,https://scrap.kakaocdn.net/dn/Js58b/hyZPpseWen/k8tUhGWa8iftUbdoY9mI01/img.png?width=486&amp;amp;height=344&amp;amp;face=0_0_486_344&quot;&gt;&lt;a href=&quot;https://www.catchsecu.com/archives/22374&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.catchsecu.com/archives/22374&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cxxQF4/hyZPBsGcpI/FL9dLC3OoxCU5AGzOih481/img.png?width=486&amp;amp;height=344&amp;amp;face=0_0_486_344,https://scrap.kakaocdn.net/dn/Js58b/hyZPpseWen/k8tUhGWa8iftUbdoY9mI01/img.png?width=486&amp;amp;height=344&amp;amp;face=0_0_486_344');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;2024년 대기업 개인정보 문제&amp;hellip;카카오톡, 카카오페이 개인정보 총정리 | 캐치시큐&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요. 캐치시큐입니다. 카카오의 개인정보 문제가 가장 큰 이슈였는데요. 카카오페이의 동의 없는 개인정보 제공과 카카오톡의 개인정보 유출 사건입니다. 카카오톡의 경우 국내 기업 중&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.catchsecu.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;K사가 의존성 높은(Dependable) 소프트웨어를 개발하지 못한 원인은 &lt;b&gt;검증(Verification)&lt;/b&gt;과 &lt;b&gt;확인(Validation)&lt;/b&gt;의 차이에서 찾을 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Verification(검증) 관점:&lt;/b&gt; K사는 초기 설계 명세서에 따라 임시 ID 생성 로직을 구현했습니다. 즉, &quot;명세서대로 올바르게 만들었는가(Are we building the product right?)&quot;에 대해서는 성공했다고 볼 수 있어, 회사 측은 문제가 없다고 주장하는 것입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Validation(확인) 관점:&lt;/b&gt; 그러나 &lt;span style=&quot;color: #006dd7;&quot;&gt;사용자가 진정으로 원했던 요구사항은 '완벽한 익명성 보장'&lt;/span&gt;이었습니다. 결과적으로 개인정보가 유출되었으므로, &quot;올바른 제품을 만들었는가(Are we building the right product?)&quot;에 대한 &lt;b&gt;Validation(확인)에는 실패&lt;/b&gt;한 것입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 K사는 명세서 준수 여부(Verification)에만 집중하고, 실제 사용자의 핵심 요구사항인 익명성이 보장되는지(Validation)를 충분히 검토하지 못한 것이 근본 원인입니다. 이렇듯 V &amp;amp; V test를 만족시키지 못하면 실제 사용자 요구사항을 반영하지 못해 해당 해당 서비스는 큰 손실로 이어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 정적 검증 vs 동적 검증&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어를 검사하는 방법은 크게 두 가지로 나뉩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 소프트웨어 인스펙션 (Software Inspection) - 정적 검증&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;방식:&lt;/b&gt; 프로그램을 실행하지 않고, 소스 코드나 문서를 &lt;b&gt;눈으로 보고 분석&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점:&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;시스템을 실행하기 전(초기 단계)에 오류를 잡을 수 있습니다.&lt;/li&gt;
&lt;li&gt;테스트하다가 하나 터지면 멈춰야 하는 동적 테스트와 달리, 한 번 검토할 때 여러 오류를 동시에 찾을 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실무 예시:&lt;/b&gt; GitHub의 &lt;b&gt;PR(Pull Request) 코드 리뷰&lt;/b&gt;, 정적 분석 툴(Lint) 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 한 두번 해봤다면 알게 모르게 Lint를 사용해봤을 것입니다. 여기서 Lint에 대해 조금 자세히 살펴보겠습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Lint란?&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; 소스 코드를 실행하지 않고(정적), 프로그래밍 오류, 버그, 스타일 오류, 의심스러운 구조 등을 찾아내는 도구입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;어원:&lt;/b&gt; 옷에 붙은 보풀(Lint)을 떼어내는 도구에서 유래했습니다. 코드에 붙은 지저분한 보풀(버그, 안 좋은 패턴)을 제거한다는 뜻입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 목적:&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;버그 예방:&lt;/b&gt; 실행 전 컴파일러가 잡지 못하는 잠재적 오류(Null Pointer, 무한 루프 등) 식별.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코딩 컨벤션 준수:&lt;/b&gt; 팀원 간 코드 스타일 통일 (들여쓰기, 변수 명명법 등).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코드 품질 향상:&lt;/b&gt; 복잡도 관리, 사용하지 않는 변수 제거 등.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 각 언어 생태계에서 가장 많이 쓰이는 '국룰' 도구들입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;① Python (파이썬)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 인터프리터 언어라 실행 전 오류를 잡는 게 중요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;대표 도구:&lt;/b&gt; Pylint, Flake8, Black(포맷터)&lt;/li&gt;
&lt;li&gt;  사용 예시 (Pylint):Pythonprint(add(1, 2))`&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765939205442&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# bad_code.py
def add(x, y):
    result = x + y  # 변수 사용 후 반환 안 함 (Pylint 경고)
    return

print(add(1, 2))&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Lint 실행 결과:&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;R1710: &lt;span style=&quot;color: #ee2323;&quot;&gt;Either all return statements in a function should return an expression, or none of them should.&lt;/span&gt; (inconsistent-return-statements) (함수가 값을 반환하다가 말다가 합니다. 일관성 있게 수정하세요.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;② JavaScript / TypeScript (자바스크립트)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 프론트엔드 생태계의 필수품입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;대표 도구:&lt;/b&gt; ESLint, Prettier(스타일)&lt;/li&gt;
&lt;li&gt;  사용 예시 (ESLint):JavaScript&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765939286104&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// bad_code.js
var x = 10;
if (x == &quot;10&quot;) {  // 느슨한 비교(==) 사용 (ESLint 경고)
  console.log(&quot;Equal&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Lint 실행 결과:&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Expected &lt;span style=&quot;color: #ee2323;&quot;&gt;'===' and instead saw '=='.&lt;/span&gt; (eq) (타입까지 비교하는 ===를 사용하세요. ==는 예기치 않은 버그를 유발합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;③ Java (자바)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔터프라이즈 환경에서 코드 품질과 스타일을 엄격하게 관리합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;대표 도구:&lt;/b&gt; SonarQube (종합 품질 관리), Checkstyle (스타일), PMD (버그 패턴), SpotBugs&lt;/li&gt;
&lt;li&gt;  사용 예시 (PMD):Java&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765939347553&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// BadCode.java
public class BadCode {
    public void doSomething() {
        try {
            // ... something
        } catch (Exception e) {
            // 아무것도 안 함 (Empty Catch Block)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Lint 실행 결과:&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;EmptyCatchBlock: &lt;span style=&quot;color: #ee2323;&quot;&gt;Avoid empty catch blocks&lt;/span&gt; (예외를 잡고 아무 처리도 하지 않으면 에러 추적이 불가능합니다. 로그라도 남기세요.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;④ C / C++&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 관리와 미정의 동작(Undefined Behavior)을 잡는 데 집중합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;대표 도구:&lt;/b&gt; Clang-Tidy, Cppcheck&lt;/li&gt;
&lt;li&gt;  사용 예시 (Cppcheck):C++&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765939383868&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// bad_code.cpp
void func() {
    int a[10];
    a[10] = 0; // 배열 범위 초과 (Out of bounds)
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Lint 실행 결과:&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;error: Array 'a[10]' accessed at index 10, which is out of bounds. (&lt;span style=&quot;color: #ee2323;&quot;&gt;배열 인덱스는 0~9까지입니다. 10번지에 접근하면 메모리 오염이 발생합니다.&lt;/span&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 보통 2가지 방법으로 활용됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;IDE 연동:&lt;/b&gt; VS Code나 IntelliJ 같은 에디터에 Lint 플러그인을 설치하면 코드를 짤 때 &lt;b&gt;실시간으로 빨간 줄&lt;/b&gt;을 그어주어 즉시 수정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CI/CD 파이프라인:&lt;/b&gt; GitHub Actions 등에 Lint 검사를 넣어두면, &lt;b&gt;Lint를 통과하지 못한 코드는 아예 배포(Merge)되지 않도록&lt;/b&gt; 강제하여 코드 품질을 유지합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 소프트웨어 테스팅 (Software Testing) - 동적 검증&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;방식:&lt;/b&gt; 실제 데이터를 넣고 프로그램을 &lt;b&gt;실행(Run)&lt;/b&gt;해서 동작을 관찰합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; 성능이나 신뢰성 같은 비기능적 요구사항은 실행해 봐야만 알 수 있으므로 테스팅이 필수적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 개발 테스팅 (Development Testing)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 코드를 짜는 동안(배포 전) 수행하는 모든 테스트 활동입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 유닛 테스팅 (Unit Testing)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; 개별 객체나 메서드 같은 작은 단위(Unit)를 테스트합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; 다른 부분과 격리하여 독립적으로 검사합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코드 작성 구조&lt;/b&gt;: Setup, Call, Assertion, Tear-down&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전략: &lt;/b&gt;파티션 테스트(Partition Test), 가이드라인 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 유닛테스트는 특히&amp;nbsp; 간단한 프로젝트에서 최소한의 테스트로 많이 사용하는 편입니다. 실제로 사용하는 &lt;b&gt;테스트 코드 작성 구조&lt;/b&gt;와 &lt;b&gt;테스트 전략&lt;/b&gt;에 대해 자세히 살펴봅시다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Unit Test 구조&lt;/blockquote&gt;
&lt;pre id=&quot;code_1765943682123&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

// 1. 테스트 대상 클래스 (실제 구현 코드)
class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

// 2. 테스트 클래스
class CalculatorTest {

    private Calculator calc;

    // [1] Setup (준비): 각 테스트가 실행되기 직전에 실행됨
    @BeforeEach
    void setUp() {
        System.out.println(&quot;\n[Setup] 계산기 인스턴스를 생성합니다.&quot;);
        calc = new Calculator();
    }

    // [2] Call (호출) &amp;amp; [3] Assertion (검증)
    @Test
    void testAddTwoNumbers() {
        System.out.println(&quot;[Test] 덧셈 기능을 테스트합니다.&quot;);

        // [2] Call: 실제 메서드를 실행
        int result = calc.add(3, 5);

        // [3] Assertion: 결과가 예상값(8)과 같은지 검증
        assertEquals(8, result, &quot;3 + 5는 8이어야 합니다.&quot;);
    }

    // [4] Teardown (정리): 각 테스트가 끝난 직후에 실행됨
    @AfterEach
    void tearDown() {
        System.out.println(&quot;[Teardown] 자원을 정리합니다.&quot;);
        calc = null;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 테스트 전략을 사용하던 보통 위의 코드처럼 4가지 구조(Setup, Call, Assertion, Teardown)를 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Setup (@BeforeEach)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 메서드가 실행되기 &lt;b&gt;전&lt;/b&gt;에 매번 자동으로 실행됩니다.&lt;/li&gt;
&lt;li&gt;calc = new Calculator();를 통해 깨끗한 상태의 객체를 준비합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Call&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;calc.add(3, 5)를 호출하여 테스트하려는 실제 기능을 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Assertion (assertEquals)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;assertEquals(기대값, 실제값) 메서드로 결과가 맞는지 확인합니다.&lt;/li&gt;
&lt;li&gt;값이 다르면 테스트는 Fail(실패)로 기록됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Teardown (@AfterEach)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,3,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 메서드가 실행된 &lt;b&gt;후&lt;/b&gt;에 매번 자동으로 실행됩니다.&lt;/li&gt;
&lt;li&gt;사용한 객체를 초기화하거나, 열려 있는 리소스를 닫는 등의 뒷정리를 합니다.&lt;/li&gt;
&lt;li&gt;각 테스트가 이전 테스트에 영향을 받지 않고 독립적으로 실행되도록 보장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 기본적인 테스트 구조를 알았으니 두 가지 전략을 살펴봅시다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Unit Test 전략 1: Partition Test&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션 테스트에서는 입력 데이터와 출력 결과를 공통된 특성을 가진 &lt;b&gt;동등 분할(Equivalence Partition)&lt;/b&gt; 그룹으로 나눕니다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;각 그룹에서 대표값을 뽑아 테스트하면 전체를 테스트한 효과를 냅니다. 파티션 테스트 전략을 사용하면 모든 입력가능한 값을 다 실행해보지 않고 대푯값만 테스트함으로써 유닛 테스트의 효율성을 극대화할 수 있습니다. 아래는 유명한 GCD(최소 공배수 구하기) 코드입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765944069329&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pytest

def gcd(a, b):
    # a가 b보다 작으면 두 값을 교체하여 a &amp;gt;= b가 되도록 함
    if a &amp;lt; b:
        a, b = b, a 
    
    if b == 0:
        return a
    else:
        return gcd(b, a % b)


@pytest.mark.parametrize(&quot;a, b, expected&quot;, [
    (4, 2, 2),
    (9, 3, 3),
    (4, 8, 4),
    (12, 18, 6)
])
def test_gcd(a, b, expected):
    assert gcd(a, b) == expected&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 테스트처럼 &lt;span style=&quot;color: #ee2323;&quot;&gt;input&lt;/span&gt; &lt;span style=&quot;color: #cf5148;&quot; data-token-index=&quot;1&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;모두 b가 GCD에 해당하는 경우[(4, 2), (9, 3)]&lt;/span&gt;&lt;/span&gt;뿐만 아니라&lt;span style=&quot;color: #cf5148;&quot; data-token-index=&quot;3&quot;&gt;&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;output의 partition도 고려&lt;/span&gt;&lt;/span&gt;해줄 필요가 있습니다. 따라서 &lt;span style=&quot;color: #006dd7;&quot;&gt;a가 GCD이거나 a, b가 아닌 다른 수가 gcd인 경우에 대한 테스트를[(4, 8, 4)와 (12, 18, 6)] 균등하게 진행하여 output의 결과도 고려&lt;/span&gt;해 주어야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Unit Test 전략 2: 가이드라인 테스트&lt;/blockquote&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가이드라인 기반 테스팅(Guideline-based Testing)&lt;/b&gt;은 이름 그대로 &quot;이런 상황에서 에러가 자주 나더라&quot;라는 &lt;b&gt;경험적인 지침(Guideline)&lt;/b&gt;을 바탕으로 테스트 케이스를 만드는 전략입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;파티션 테스팅이 수학적이고 논리적인 분류라면, 가이드라인 테스팅은 개발자의 &lt;b&gt;'경험'과 '직관&lt;/b&gt;'에 조금 더 의존하는 실전 팁 모음집이라고 보시면 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; 프로그래머들이 흔히 저지르는 실수 패턴을 분석하여, 그 실수가 발생할 법한 상황을 집중적으로 테스트하는 방법입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; &quot;여기쯤 버그가 숨어있겠지?&quot;라고 의심 가는 곳을 콕 집어서 공격하는 방식입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;이 책에서 제시하는 대표적인 가이드라인은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;버퍼 오버플로우:&lt;/b&gt; 입력 허용량보다 훨씬 긴 데이터를 넣어본다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;극단적인 계산 결과:&lt;/b&gt; 계산 결과가 너무 크거나 작아서 변수에 다 담기지 못하게 유도한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;에러 메시지 확인:&lt;/b&gt; 시스템이 낼 수 있는 모든 종류의 에러 메시지를 한 번씩 다 뜨게 만들어본다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반복 입력:&lt;/b&gt; 똑같은 입력을 연달아 계속 넣어본다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;가이드라인 테스팅을 적용해 회원가입 기능을 테스트한다고 가정해 봅시다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;가이드라인 ① (버퍼 테스트):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;상황:&lt;/i&gt; 아이디 입력창은 보통 20자 제한임.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;테스트:&lt;/i&gt; 여기에 &lt;b&gt;1,000자의 글자&lt;/b&gt;를 복사+붙여넣기 해본다. (서버가 뻗거나 앱이 종료되는지 확인)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가이드라인 ② (반복 테스트):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;상황:&lt;/i&gt; '가입 완료' 버튼이 있음.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;테스트:&lt;/i&gt; 버튼을 &lt;b&gt;1초에 10번 다다다닥! 클릭&lt;/b&gt;해본다. (중복 가입이 되거나 DB 에러가 나는지 확인)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가이드라인 ③ (계산 테스트):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;상황:&lt;/i&gt; 나이 입력칸.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;테스트:&lt;/i&gt; &lt;b&gt;999999999살&lt;/b&gt;을 넣어본다. (숫자형 변수 범위를 넘어서는지 확인)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;즉, 파티션 테스팅이 &quot;정석적인 검사&quot;라면, 가이드라인 테스팅은 &lt;b&gt;&quot;일부러 고장 내보는 스트레스 테스트&quot;&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 컴포넌트 테스팅 (Component Testing)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; 여러 유닛을 합쳐서 만든 컴포넌트(인터페이스)가 잘 작동하는지 테스트합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; 컴포넌트 내부 구현보다는 &lt;b&gt;인터페이스(입출력)&lt;/b&gt;가 명세대로 동작하는지에 집중합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 시스템 테스팅 (System Testing)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; 모든 컴포넌트를 통합한 &lt;b&gt;전체 시스템&lt;/b&gt;을 테스트합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; 컴포넌트 간의 상호작용 오류를 찾고, 성능이나 보안 같은 비기능적 요구사항도 이때 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 테스트 주도 개발 (TDD: Test-Driven Development)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;코드를 짜고 테스트하는 게 아니라, &lt;b&gt;테스트를 먼저 짜고 코드를 채운다&lt;/b&gt;&quot;는 혁신적인 방법론입니다. TDD 또한 내용이 너무 방대하고 많아 자세히 접해보고 싶다면 최범균님의 아래 영상을 참고하시는 것을 권장드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=WBVjBwKx47I&amp;amp;list=PLwouWTPuIjUj_QqgXlFsqjUwyC0-5dZ_q&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=WBVjBwKx47I&amp;amp;list=PLwouWTPuIjUj_QqgXlFsqjUwyC0-5dZ_q&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=WBVjBwKx47I&amp;amp;list=PLwouWTPuIjUj_QqgXlFsqjUwyC0-5dZ_q&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bzQDqh/hyZPDppLDl/S75cgF2ukU5oWuxpM9lUKk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=1148_616_1194_666,https://scrap.kakaocdn.net/dn/ogolL/hyZPPwBPrJ/NhFzKgXNfvIzoCsSzXVYNK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=1148_616_1194_666&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;세미나 공유 - 테스트 가능 구조&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/WBVjBwKx47I&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) TDD 프로세스&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;기능 식별:&lt;/b&gt; 구현할 작은 기능 하나를 정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 작성:&lt;/b&gt; 그 기능을 검증하는 테스트 코드를 먼저 짭니다. (아직 기능이 없으니 실행하면 당연히 &lt;b&gt;실패/Fail&lt;/b&gt;  )&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코드 구현:&lt;/b&gt; 테스트를 통과할 수 있는 최소한의 코드를 작성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 실행:&lt;/b&gt; 테스트가 &lt;b&gt;성공/Pass&lt;/b&gt;하는지 확인합니다. ( )&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리팩토링:&lt;/b&gt; 코드를 깔끔하게 다듬고 다시 테스트합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) TDD의 장점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;코드 커버리지:&lt;/b&gt; 모든 코드에 대해 테스트가 자연스럽게 만들어집니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디버깅 시간 단축:&lt;/b&gt; 에러가 나면 방금 짠 그 코드만 보면 되니까 원인 찾기가 쉽습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문서화 효과:&lt;/b&gt; 테스트 코드 자체가 &quot;이 기능은 이렇게 쓰는 거야&quot;라고 알려주는 예제 역할을 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 릴리즈 테스팅 (Release Testing)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발팀이 아니라 별도의 &lt;b&gt;테스트 팀&lt;/b&gt;이 수행하는 단계입니다. 고객에게 배포하기 전에 &quot;진짜 써도 되는지&quot; 검증합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 요구사항 기반 테스팅&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요구사항 명세서를 펴놓고 하나씩 체크합니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;예시:&lt;/i&gt; &quot;사용자가 로그인하면 메인 페이지로 이동해야 한다&quot; -&amp;gt; 로그인 시도 -&amp;gt; 메인 페이지 뜨는지 확인.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 시나리오 테스팅&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 사용자가 쓰는 것처럼 시나리오를 짜서 테스트합니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;예시(사진 앱):&lt;/i&gt; 사진 찍기 -&amp;gt; 필터 적용 -&amp;gt; 저장 -&amp;gt; SNS 공유 -&amp;gt; 잘 올라갔는지 확인.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 사용자 테스팅 (User Testing)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무리 우리가 테스트를 잘해도, 실제 사용자의 환경이나 데이터는 예측할 수 없습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 알파 테스팅 (Alpha Testing)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발사 내부의 다른 직원이나 소수의 사용자가 개발 환경에서 테스트합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 베타 테스팅 (Beta Testing)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소프트웨어의 &lt;b&gt;초기 버전(Beta)&lt;/b&gt;을 일반 사용자에게 공개하여 써보게 합니다.&lt;/li&gt;
&lt;li&gt;사용자들의 실제 피드백과 버그 리포트를 받아 수정합니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;예시:&lt;/i&gt; 게임 출시 전 '오픈 베타 테스트(OBT)'.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 인수 테스팅 (Acceptance Testing)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고객(발주사)이 &quot;이 정도면 돈 주고 살만하다(인수하겠다)&quot;라고 결정하기 위해 수행하는 최종 테스트입니다.&lt;/li&gt;
&lt;li&gt;이 테스트를 통과해야 정식으로 납품이 완료됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스팅은 버그를 찾는 &lt;b&gt;Defect Testing&lt;/b&gt;과 요구사항 만족을 확인하는 &lt;b&gt;Validation Testing&lt;/b&gt;으로 나뉩니다. 개발자는 &lt;b&gt;유닛-컴포넌트-시스템 테스팅&lt;/b&gt;을 거치며, 최근에는 &lt;b&gt;TDD&lt;/b&gt;를 통해 코드 품질을 높이는 추세입니다. 마지막으로 릴리즈 및 사용자 테스팅을 통해 최종 검증을 마쳐야 비로소 좋은 소프트웨어가 탄생합니다!&lt;/p&gt;</description>
      <category>Computer Science/SW Engineering</category>
      <category>SW Engineering</category>
      <category>SW 공학</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/267</guid>
      <comments>https://wondrous-developer.tistory.com/267#entry267comment</comments>
      <pubDate>Wed, 17 Dec 2025 11:17:48 +0900</pubDate>
    </item>
    <item>
      <title>[SW Engineering] 07. 디자인과 구현(Design and Implement)</title>
      <link>https://wondrous-developer.tistory.com/266</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처가 건물의 뼈대라면, &lt;b&gt;설계 및 구현&lt;/b&gt;은 실제 배관, 전기 배선, 인테리어를 하고 벽돌을 쌓는 과정입니다. 이번 포스팅에서는 객체지향 설계 프로세스, 디자인 패턴, 그리고 구현 단계의 주요 이슈들을 정리합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 설계와 구현의 관계&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; 소프트웨어 설계와 구현은 실행 가능한 소프트웨어 시스템을 개발하는 단계입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상호작용:&lt;/b&gt; 설계(Design)와 구현(Implementation)은 칼로 자르듯 나뉘지 않고, 서로 &lt;b&gt;맞물려(Inter-leaved)&lt;/b&gt; 진행됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;설계:&lt;/b&gt; &quot;쇼핑몰의 장바구니 기능은 어떤 데이터가 필요하고, 결제 시스템과는 어떻게 연결될까?&quot;를 고민하는 창의적 활동.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구현:&lt;/b&gt; 설계를 바탕으로 실제 Java나 Python 코드를 작성하는 과정.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 직접 개발 vs 구매 (Build or buy)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 소프트웨어 공학에서는 모든 것을 바닥부터 개발하지 않습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;COTS (Commercial-off-the-shelf):&lt;/b&gt; 시중에 나와 있는 솔루션이나 패키지를 구매하여 내 입맛에 맞게 설정(Configuration)하여 사용하는 경우가 많습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  예시:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;직접 개발 (Build):&lt;/b&gt; 우리 회사만의 독특한 결제 로직이 필요해서 결제 시스템을 처음부터 코딩함. (비쌈, 오래 걸림)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구매 (Buy):&lt;/b&gt; 병원에서 쓸 환자 관리 시스템이 필요할 때, 직접 개발하는 대신 이미 잘 만들어진 '의료용 ERP 솔루션'을 사서 우리 병원 이름만 넣고 씀. (저렴, 빠름)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 객체지향 설계 (Object-Oriented Design)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향 설계는 시스템을 상호작용하는 객체들의 집합으로 모델링하는 프로세스입니다. 주로 UML(Unified Modeling Language)을 사용합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 주요 프로세스 단계와 예시 (쇼핑몰 시스템)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 글에서는 '기상 관측소(Weather Station)'를 예로 들지만, 더 와닿는 &lt;b&gt;쇼핑몰&lt;/b&gt;로 설명해 드릴게요.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;시스템 문맥 파악 (Context &amp;amp; Modes of use):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템의 경계(Boundary)를 정합니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;예시:&lt;/i&gt; &quot;우리 쇼핑몰 시스템은 &lt;b&gt;택배사 시스템&lt;/b&gt;, &lt;b&gt;은행 결제 시스템&lt;/b&gt;과 연결되어야 해.&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;아키텍처 설계 (Architecture Design):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주요 컴포넌트를 식별합니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;예시:&lt;/i&gt; &quot;웹 서버, 고객 데이터베이스, 상품 관리 모듈로 나누자.&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;객체 식별 (Object Identification):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템 내의 주요 객체를 찾아냅니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;예시:&lt;/i&gt; &lt;b&gt;고객(Customer), 상품(Product), 주문(Order), 장바구니(Cart)&lt;/b&gt; 등이 객체가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디자인 모델 개발 (Design Models):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구조와 동작을 모델링합니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;예시:&lt;/i&gt; &quot;고객이 '주문 버튼'을 누르면(Sequence), 주문 객체가 생성되고 상품 재고가 1 줄어든다(State).&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인터페이스 명세 (Interface Specification):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체 간의 대화 규칙을 정합니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;예시:&lt;/i&gt; &quot;주문 객체는 결제 객체에게 &lt;code&gt;processPayment(amount)&lt;/code&gt;라는 함수만 호출할 수 있다.&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 객체를 식별하는 방법 (Approaches to identification)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 찾는 데 정해진 공식(Magic formula)은 없지만, 다음 팁을 활용하세요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;문법적 접근:&lt;/b&gt; 요구사항 명세서에서 &lt;b&gt;명사(Noun)&lt;/b&gt;는 객체로, &lt;b&gt;동사(Verb)&lt;/b&gt;는 메서드(행동)로 봅니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;*&quot;고객(명사)이 상품(명사)을 주문한다(동사)&quot; &amp;rarr; Customer, Product 객체 / order() 메서드*&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유형물 기반:&lt;/b&gt; 현실 세계의 물리적인 사물(자동차, 센서 등)을 그대로 객체로 만듭니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 객체 지향 설계에 도움이 되는 책&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001628109?utm_source=google&amp;amp;utm_medium=cpc&amp;amp;utm_campaign=googleSearch&amp;amp;gt_network=g&amp;amp;gt_keyword=&amp;amp;gt_target_id=aud-901091942354:dsa-435935280379&amp;amp;gt_campaign_id=9979905549&amp;amp;gt_adgroup_id=132556570510&amp;amp;gad_source=1&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://product.kyobobook.co.kr/detail/S000001628109?utm_source=google&amp;amp;utm_medium=cpc&amp;amp;utm_campaign=googleSearch&amp;amp;gt_network=g&amp;amp;gt_keyword=&amp;amp;gt_target_id=aud-901091942354:dsa-435935280379&amp;amp;gt_campaign_id=9979905549&amp;amp;gt_adgroup_id=132556570510&amp;amp;gad_source=1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1765936000923&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;객체지향의 사실과 오해 | 조영호 - 교보문고&quot; data-og-description=&quot;객체지향의 사실과 오해 | 객체지향에 대한 선입견을 버려라!『객체지향의 사실과 오해』는 객체지향이란 무엇인가라는 원론적면서도 다소 위험한 질문에 답하기 위해 쓰여진 책이다. 안타깝&quot; data-og-host=&quot;product.kyobobook.co.kr&quot; data-og-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001628109?utm_source=google&amp;amp;utm_medium=cpc&amp;amp;utm_campaign=googleSearch&amp;amp;gt_network=g&amp;amp;gt_keyword=&amp;amp;gt_target_id=aud-901091942354:dsa-435935280379&amp;amp;gt_campaign_id=9979905549&amp;amp;gt_adgroup_id=132556570510&amp;amp;gad_source=1&quot; data-og-url=&quot;https://product.kyobobook.co.kr/detail/S000001628109&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cZMBir/hyZPxKA6WI/POL4Je4KNAVwCVQVmP2lj0/img.jpg?width=458&amp;amp;height=628&amp;amp;face=0_0_458_628,https://scrap.kakaocdn.net/dn/edpvMI/hyZPzIpYye/SdH41lHYdisy5kfukzvmh1/img.jpg?width=458&amp;amp;height=628&amp;amp;face=0_0_458_628,https://scrap.kakaocdn.net/dn/caBnJC/hyZPNlfQeb/PiKBdfGDOuvBolkkBcHsfk/img.png?width=335&amp;amp;height=335&amp;amp;face=0_0_335_335&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001628109?utm_source=google&amp;amp;utm_medium=cpc&amp;amp;utm_campaign=googleSearch&amp;gt;_network=g&amp;gt;_keyword=&amp;gt;_target_id=aud-901091942354:dsa-435935280379&amp;gt;_campaign_id=9979905549&amp;gt;_adgroup_id=132556570510&amp;amp;gad_source=1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001628109?utm_source=google&amp;amp;utm_medium=cpc&amp;amp;utm_campaign=googleSearch&amp;gt;_network=g&amp;gt;_keyword=&amp;gt;_target_id=aud-901091942354:dsa-435935280379&amp;gt;_campaign_id=9979905549&amp;gt;_adgroup_id=132556570510&amp;amp;gad_source=1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cZMBir/hyZPxKA6WI/POL4Je4KNAVwCVQVmP2lj0/img.jpg?width=458&amp;amp;height=628&amp;amp;face=0_0_458_628,https://scrap.kakaocdn.net/dn/edpvMI/hyZPzIpYye/SdH41lHYdisy5kfukzvmh1/img.jpg?width=458&amp;amp;height=628&amp;amp;face=0_0_458_628,https://scrap.kakaocdn.net/dn/caBnJC/hyZPNlfQeb/PiKBdfGDOuvBolkkBcHsfk/img.png?width=335&amp;amp;height=335&amp;amp;face=0_0_335_335');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;객체지향의 사실과 오해 | 조영호 - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;객체지향의 사실과 오해 | 객체지향에 대한 선입견을 버려라!『객체지향의 사실과 오해』는 객체지향이란 무엇인가라는 원론적면서도 다소 위험한 질문에 답하기 위해 쓰여진 책이다. 안타깝&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;product.kyobobook.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향은 그 자체만으로 내용이 너무 방대하고 많기 때문에 이 책을 읽으면서 각 객체의 책임과 역할을 어떻게 할당할지 고민해보는 것을 권장합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 디자인 패턴 (Design Patterns)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인 패턴은 선배 개발자들이 수많은 시행착오 끝에 찾아낸 &lt;b&gt;&quot;자주 발생하는 문제에 대한 모범 답안(Best Practice)&quot;&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 대표 패턴: 옵저버 (Observer Pattern)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상황:&lt;/b&gt; 하나의 데이터(주제)가 변했을 때, 이를 지켜보는 여러 화면(관찰자)들이 동시에 업데이트되어야 할 때.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  구체적 예시 (유튜브 구독 알림):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Subject (유튜버):&lt;/b&gt; &quot;새 영상이 올라왔습니다!&quot;라고 상태를 변경함.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Observer (구독자들):&lt;/b&gt; 수백만 명의 구독자 스마트폰에 동시에 '딩동!' 하고 알림이 뜸.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;핵심:&lt;/i&gt; 유튜버는 누가 구독했는지 일일이 알 필요 없이, 그냥 &quot;알림 보내기&quot;만 하면 구독자들이 알아서 반응함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 기타 유용한 패턴&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Facade (건물 외벽):&lt;/b&gt; 복잡한 내부 배선은 감추고 깔끔한 스위치 하나만 밖으로 빼주는 것.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;예:&lt;/i&gt; 복잡한 DB 연결, 로깅, 보안 체크 코드를 다 숨기고 &lt;code&gt;login()&lt;/code&gt; 함수 하나만 제공.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Iterator (반복자):&lt;/b&gt; 책장에 책이 어떻게 꽂혀있든(배열이든 리스트든) 상관없이 &quot;다음 책, 그다음 책&quot; 순서대로 꺼낼 수 있게 해주는 것.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Decorator (장식):&lt;/b&gt; 빵(기본 객체) 위에 생크림을 얹고(기능 추가), 그 위에 체리를 얹는(기능 또 추가) 것처럼 실행 중에 기능을 덧붙이는 패턴.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 구현 이슈 (Implementation Issues)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 외에도 구현 단계에서 중요하게 다뤄야 할 3가지 이슈가 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 재사용 (Reuse)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 소프트웨어는 레고 블록 조립과 같습니다. 남이 만든 좋은 블록(라이브러리)이 있다면 가져다 쓰는 게 이득입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;비용:&lt;/b&gt; 단순히 공짜가 아닙니다. 내 프로젝트에 맞는지 &lt;b&gt;찾는 시간&lt;/b&gt;, &lt;b&gt;배우는 시간&lt;/b&gt;, 안 맞으면 &lt;b&gt;수정하는 비용&lt;/b&gt;을 고려해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 형상 관리 (Configuration Management)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수많은 코드 변경 내역을 관리하는 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; ️ 예시 (Git):&lt;/b&gt; &quot;어제 짠 코드가 더 나은데? 되돌리자.&quot;라거나, &quot;철수가 짠 코드랑 내가 짠 코드가 충돌났네?&quot; 같은 상황을 해결해 주는 도구입니다. 버전 관리, 시스템 통합, 버그 추적 등을 포함합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 호스트-타겟 개발 (Host-target development)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상황:&lt;/b&gt; 개발은 고성능 데스크탑(Host)에서 하지만, 실제 프로그램은 스마트폰이나 자동차 내비게이션(Target)에서 돌아가는 경우입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이슈:&lt;/b&gt; 내 컴퓨터(Host)에선 잘 돌아가는데, 성능이 낮은 스마트폰(Target)에선 버벅거리거나 배터리를 광탈시킬 수 있으므로 이를 고려해 시뮬레이터 등을 사용해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 오픈소스 개발 (Open Source Development)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 코드를 전 세계에 공개하고 자원봉사자들과 함께 개발하는 방식입니다 (예: Linux, Java, Apache). 하지만 &lt;b&gt;&quot;공짜라고 막 갖다 쓰면 큰일&quot;&lt;/b&gt; 날 수 있습니다. 바로 &lt;b&gt;라이선스&lt;/b&gt; 때문입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라이선스 모델 (License Models) 비교&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;라이선스&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;비유 (요리 레시피)&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;의무 사항&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GPL&lt;/b&gt; (엄격)&lt;/td&gt;
&lt;td&gt;&quot;내 비법 소스 썼어? 그럼 너도 네 요리 &lt;b&gt;전체 레시피를 무조건 공개&lt;/b&gt;해!&quot;&lt;/td&gt;
&lt;td&gt;내가 만든 소프트웨어의 전체 소스 코드도 무조건 공개해야 함 (전염성).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LGPL&lt;/b&gt; (중간)&lt;/td&gt;
&lt;td&gt;&quot;내 소스를 그대로 썼으면 그것만 공개하고, 네가 덧붙인 요리법은 비밀로 해도 돼.&quot;&lt;/td&gt;
&lt;td&gt;라이브러리 자체를 수정한 경우만 공개. 내 프로그램 소스는 공개 안 해도 됨.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;BSD / MIT&lt;/b&gt; (관대)&lt;/td&gt;
&lt;td&gt;&quot;내 레시피 맘대로 써. 대신 메뉴판 구석에 &lt;b&gt;'원조: 김씨네'&lt;/b&gt;라고 이름만 써줘.&quot;&lt;/td&gt;
&lt;td&gt;소스 공개 의무 없음. 상용 소프트웨어에 자유롭게 포함해서 팔아도 됨.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요약:&lt;/b&gt; 설계와 구현은 반복적인 과정입니다. &lt;b&gt;'쇼핑몰'&lt;/b&gt;을 만든다고 상상하며 객체를 찾고(OOD), &lt;b&gt;'유튜브 알림'&lt;/b&gt; 같은 상황에 옵저버 패턴을 적용하며, 남이 만든 코드(Reuse/Open Source)를 가져다 쓸 때는 &lt;b&gt;'저작권(라이선스)'&lt;/b&gt;을 꼼꼼히 확인하는 것이 훌륭한 엔지니어의 자세입니다!&lt;/p&gt;</description>
      <category>Computer Science/SW Engineering</category>
      <category>SW Engineering</category>
      <category>SW 공학</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/266</guid>
      <comments>https://wondrous-developer.tistory.com/266#entry266comment</comments>
      <pubDate>Wed, 17 Dec 2025 10:41:57 +0900</pubDate>
    </item>
    <item>
      <title>[SW Engineering] 06. 아키텍쳐 설계(Architectural Design)</title>
      <link>https://wondrous-developer.tistory.com/265</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;건물을 지을 때 설계도가 가장 중요하듯, 소프트웨어 개발에서도 &lt;b&gt;아키텍처 설계&lt;/b&gt;는 시스템의 성패를 좌우하는 핵심 단계입니다. 이번 포스팅에서는 아키텍처 설계의 정의와 중요성, 그리고 실무에서 자주 쓰이는 주요 아키텍처 패턴들을 정리해 봅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 아키텍처 설계란? (Architectural Design)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처 설계는 소프트웨어 시스템을 어떻게 조직할지 이해하고, 그 &lt;b&gt;전체적인 구조(Overall structure)&lt;/b&gt;를 설계하는 과정입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할:&lt;/b&gt; 디자인과 요구사항 공학 사이의 결정적인 연결 고리(Critical link) 역할을 합니다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 활동:&lt;/b&gt; 시스템의 주요 구조적 컴포넌트와 그들 간의 관계를 식별합니다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;애자일(Agile)에서의 위치:&lt;/b&gt; 일반적으로 애자일 프로세스에서도 초기 단계에 전체적인 시스템 아키텍처를 설계하는 것을 인정합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;이유:&lt;/i&gt; 나중에 아키텍처를 리팩토링(Refactoring)하려면 시스템의 너무 많은 컴포넌트에 영향을 주기 때문에 비용이 매우 비싸기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 아키텍처의 추상화 레벨&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Architecture in the small:&lt;/b&gt; 개별 프로그램(Program) 수준. 하나의 프로그램을 어떻게 컴포넌트로 분해할지 고민합니다 .&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Architecture in the large:&lt;/b&gt; 복잡한 엔터프라이즈 시스템 수준. 여러 다른 시스템, 프로그램 등을 포함하며, 서로 다른 회사가 관리하는 분산 환경일 수도 있습니다 .&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 명시적 아키텍처의 장점 (Advantages of explicit architecture)&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;이해관계자 의사소통 (Stakeholder communication):&lt;/b&gt; 시스템을 논의할 때 중심 초점이 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시스템 분석 (System analysis):&lt;/b&gt; 비기능적 요구사항(성능, 보안 등)을 만족시킬 수 있는지 초기에 분석 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대규모 재사용 (Large-scale reuse):&lt;/b&gt; 아키텍처 자체를 여러 시스템에 재사용하거나 제품 라인(Product-line) 아키텍처를 개발할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 아키텍처 설계 결정과 시스템 특성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처 설계는 창의적인 프로세스이지만, 모든 설계 과정에서 공통적으로 결정해야 할 사항들이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;예시:&lt;/i&gt; 어떤 일반적인 애플리케이션 아키텍처를 템플릿으로 쓸 것인가? 어떤 아키텍처 패턴을 적용할 것인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 아키텍처와 비기능적 요구사항 (System Characteristics)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 비기능적 요구사항(Non-functional Requirement)을 만족시키기 위해 아키텍처를 전략적으로 선택해야 합니다. 이는 곧 유저의 기대인 Validation과도 직접적으로 영향을 미쳐 프로젝트 성공여부를 결정하기도 합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;특성&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;아키텍처 전략&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;성능 (Performance)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;중요한 오퍼레이션을 국소화(Localise)하고 통신을 최소화합니다. 작은 컴포넌트보다는 큰(Large-grain) 컴포넌트를 사용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;보안 (Security)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;레이어드(Layered) 아키텍처를 사용하여 중요한 자산을 가장 안쪽 레이어에 둡니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;안전 (Safety)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;안전에 중요한 기능을 소수의 서브시스템에 집중시킵니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;가용성 (Availability)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;중복(Redundant) 컴포넌트를 포함하여 장애 허용(Fault tolerance) 메커니즘을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유지보수성 (Maintainable)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;변경하기 쉽도록 작고(Fine-grain) 교체 가능한 컴포넌트를 사용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 아키텍처 패턴 (Architectural Patterns)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처 패턴은 좋은 설계 관행(Good design practice)을 추상화하여 공유하고 재사용할 수 있게 만든 것입니다. 구체적인 설계도가 아니라, 다양한 방식으로 구현될 수 있는 &lt;b&gt;템플릿&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) MVC 패턴 (Model-View-Controller)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu.png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l6z6j/dJMcagKPIAG/fEas4gwkJCekTBInxeLZ00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l6z6j/dJMcagKPIAG/fEas4gwkJCekTBInxeLZ00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l6z6j/dJMcagKPIAG/fEas4gwkJCekTBInxeLZ00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl6z6j%2FdJMcagKPIAG%2FfEas4gwkJCekTBInxeLZ00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2816&quot; height=&quot;1536&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu.png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 인터페이스(View)와 데이터 처리(Model)를 분리하는 가장 유명한 패턴입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;구성 요소:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Model:&lt;/b&gt; 애플리케이션의 상태(데이터)와 비즈니스 로직을 캡슐화합니다 .&lt;/li&gt;
&lt;li&gt;&lt;b&gt;View:&lt;/b&gt; 모델을 화면에 렌더링하고 사용자 이벤트를 컨트롤러에 보냅니다 .&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Controller:&lt;/b&gt; 사용자 행동(키보드, 마우스 등)을 모델 업데이트로 매핑하고 뷰를 선택합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;웹 애플리케이션 예시:&lt;/b&gt; 브라우저(View)가 HTTP 요청을 보내면 컨트롤러가 처리하고, 모델(DB)을 업데이트한 뒤, 동적으로 페이지(View)를 생성해 응답합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 구체적인 예시를 볼까요?&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오: 맛집 웨이팅 앱 (캐치테이블/테이블링)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐치 테이블에서는 다음과 같이 &lt;b&gt; &quot;사용자의 입력을 받아(Controller), 데이터를 처리하고(Model), 화면을 그립니다.(View).&quot; &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;4,0,1,0,0,0&quot;&gt;&lt;b&gt;Controller (주문 접수원):&lt;/b&gt; 사용자가 앱에서 &lt;b&gt;'웨이팅 등록하기' 버튼을 클릭&lt;/b&gt;합니다. &lt;/span&gt;&lt;span data-path-to-node=&quot;4,0,1,0,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;4,0,1,0,0,2&quot;&gt;&lt;span&gt;컨트롤러는 이 요청을 받아 모델에게 &quot;대기열에 사람 한 명 추가해줘&quot;라고 명령하고, 등록 성공 후 보여줄 화면(View)을 선택합니다 &lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;4,0,1,0,0,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;4,0,1,0,0,4&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;4,0,1,1,0,0&quot;&gt;&lt;b&gt;Model (주방장/장부):&lt;/b&gt; 현재 &lt;b&gt;대기 팀 수(5팀), 예상 대기 시간(30분)&lt;/b&gt; 같은 핵심 데이터와 '대기 번호 발급 로직'을 가지고 있습니다. &lt;/span&gt;&lt;span data-path-to-node=&quot;4,0,1,1,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;4,0,1,1,0,2&quot;&gt;&lt;span&gt;컨트롤러의 명령을 받아 대기 명단에 데이터를 추가하고 상태를 갱신합니다 &lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;4,0,1,1,0,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;4,0,1,1,0,4&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;4,0,1,2,1,0&quot;&gt;&lt;b&gt;&lt;span&gt;View (메뉴판/전광판):&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 모델의 데이터가 변경되면, 화면에 &lt;b&gt;&quot;현재 대기 6팀, 예상 대기 시간 40분&quot;&lt;/b&gt;이라는 텍스트와 UI를 그려서 사용자에게 보여줍니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;4,0,1,2,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 계층형 아키텍처 (Layered Architecture)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu (1).png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEe1nw/dJMcajgq7df/HIHoiG7sAFaKpUk2GqQoBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEe1nw/dJMcajgq7df/HIHoiG7sAFaKpUk2GqQoBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEe1nw/dJMcajgq7df/HIHoiG7sAFaKpUk2GqQoBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEe1nw%2FdJMcajgq7df%2FHIHoiG7sAFaKpUk2GqQoBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2816&quot; height=&quot;1536&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu (1).png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템을 여러 계층(Layer)으로 나누어 구성합니다. 각 계층은 관련된 서비스 집합을 제공합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; 서브시스템의 점진적 개발을 지원합니다. &lt;b&gt;어떤 레이어의 인터페이스가 바뀌어도 인접한 레이어에만 영향을 줍니다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일반적인 구조 예시:&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자 인터페이스 (User Interface)&lt;/li&gt;
&lt;li&gt;UI 관리 / 인증 및 인가 (Authentication)&lt;/li&gt;
&lt;li&gt;핵심 비즈니스 로직 (Core business logic)&lt;/li&gt;
&lt;li&gt;시스템 지원 (OS, Database 등).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계층형 아키텍쳐에서는 &quot;각 층은 자기 할 일만 하고, 바로 아래층이나 위층 하고만 대화&quot;하여 계층간 독립성을 유지합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오: 모바일 뱅킹 앱(토스/카카오뱅크)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-path-to-node=&quot;8,0,1,0,1,0&quot;&gt;&lt;span&gt;뱅킹 앱처럼 각 계층 역할이 명확히 구분되어야 하는 서비스에서는 다음처럼 &lt;/span&gt;&lt;b&gt;&lt;span&gt;계층간 독립성을 유지합니다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;8,0,1,0,1,0&quot;&gt;&lt;b&gt;&lt;span&gt;1계층 (User Interface):&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 사용자가 &lt;/span&gt;&lt;b&gt;&lt;span&gt;'송금하기' 화면&lt;/span&gt;&lt;/b&gt;&lt;span&gt;에서 금액을 입력하고 버튼을 누르는 UI 부분입니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,0,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,0,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;8,0,1,1,0,0&quot;&gt;&lt;b&gt;2계층 (Authentication/UI Management):&lt;/b&gt; 사용자가 &lt;b&gt;로그인한 상태인지, 비밀번호가 맞는지 인증&lt;/b&gt;합니다. &lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,1,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,1,0,2&quot;&gt;&lt;span&gt;UI에서 넘어온 입력값이 숫자가 맞는지 검사합니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,1,0,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,1,0,4&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;8,0,1,2,0,0&quot;&gt;&lt;b&gt;3계층 (Core Business Logic):&lt;/b&gt; 실제 송금 로직이 돌아갑니다. &lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,2,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,2,0,2&quot;&gt;&lt;span&gt;&quot;잔액이 송금액보다 많은지 확인&quot;하고, &quot;A계좌에서 빼서 B계좌로 더하는&quot; &lt;/span&gt;&lt;b&gt;&lt;span&gt;핵심 업무&lt;/span&gt;&lt;/b&gt;&lt;span&gt;를 수행합니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,2,0,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;8,0,1,2,0,4&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot; data-path-to-node=&quot;8,0,1,3,1,0&quot;&gt;&lt;b&gt;4계층 (System Support/Database):&lt;/b&gt; 실제 은행 &lt;b&gt;데이터베이스(DB)에 접속&lt;/b&gt;해서 잔액 데이터를 수정하고 저장합니다&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot; data-path-to-node=&quot;8,0,1,3,1,1&quot;&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot; data-path-to-node=&quot;8,0,1,3,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;i&gt;특징:&lt;/i&gt; DB 구조(4계층)가 바뀌어도 송금 버튼 디자인(1계층)은 바꿀 필요가 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 리포지토리 아키텍처 (Repository Architecture)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu (2).png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTo0Kq/dJMcabJxi2u/NLGNjSsVKfyVKbgMS0xNfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTo0Kq/dJMcabJxi2u/NLGNjSsVKfyVKbgMS0xNfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTo0Kq/dJMcabJxi2u/NLGNjSsVKfyVKbgMS0xNfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTo0Kq%2FdJMcabJxi2u%2FNLGNjSsVKfyVKbgMS0xNfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2816&quot; height=&quot;1536&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu (2).png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브시스템 간에 데이터를 교환할 때 사용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;방식:&lt;/b&gt; 중앙 데이터베이스(Repository)에 공유 데이터를 두고 모든 서브시스템이 접근하게 합니다. (반대 방식: 각자가 데이터를 가지고 명시적으로 주고받기)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점:&lt;/b&gt; 대량의 데이터를 공유해야 할 때 가장 효율적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시:&lt;/b&gt; IDE(통합 개발 환경)에서 프로젝트 리포지토리를 중심으로 에디터, 컴파일러, UML 툴들이 데이터를 공유하는 구조.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레포지토리 아키텍쳐는 중앙 저장소에 자료를 두고 여러 사람이 필요할 때 각자 꺼내 쓰는 상황에서 유리합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오: 협업 문서 도구 (깃허브/구글 닥스/노션)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;12,0,1,0,0,0&quot;&gt;&lt;b&gt;중앙 리포지토리 (Central Repository):&lt;/b&gt; 우리가 작성 중인 &lt;b&gt;문서 데이터 그 자체(주로 GitHub Repository)&lt;/b&gt;가 저장된 중앙 서버입니다. &lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,0,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,0,0,2&quot;&gt;&lt;span&gt;모든 데이터는 여기에 모여 있습니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,0,0,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,0,0,4&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서브시스템 A (에디터):&lt;/b&gt; 내가 글자를 타이핑하면 중앙 저장소의 내용을 수정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서브시스템 B (댓글 알림 봇):&lt;/b&gt; 중앙 저장소에 새로운 댓글 데이터가 들어오면, 이를 감지해서 사용자에게 알림을 보냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서브시스템 C (PDF 변환기):&lt;/b&gt; 사용자가 다운로드를 요청하면, 중앙 저장소의 데이터를 가져와서 PDF 파일로 만들어줍니다.&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;12,0,1,4,1,0&quot;&gt;&lt;i&gt;&lt;span&gt;특징:&lt;/span&gt;&lt;/i&gt;&lt;span&gt; 알림 봇과 PDF 변환기는 서로 직접 대화하지 않고, 오직 &lt;/span&gt;&lt;b&gt;&lt;span&gt;중앙 저장소의 데이터&lt;/span&gt;&lt;/b&gt;&lt;span&gt;만 바라보고 일합니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,4,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,4,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4) 클라이언트-서버 아키텍처 (Client-Server Architecture)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu (3).png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccpUoG/dJMcachmohQ/i2YPLKMJSMKBxI3j3rDH3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccpUoG/dJMcachmohQ/i2YPLKMJSMKBxI3j3rDH3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccpUoG/dJMcachmohQ/i2YPLKMJSMKBxI3j3rDH3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccpUoG%2FdJMcachmohQ%2Fi2YPLKMJSMKBxI3j3rDH3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2816&quot; height=&quot;1536&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu (3).png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터와 처리를 여러 컴포넌트에 분산시키는 모델입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;구성:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;서버 (Servers):&lt;/b&gt; 프린팅, 데이터 관리 등 특정 서비스를 제공하는 독립적인 컴포넌트.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트 (Clients):&lt;/b&gt; 서비스를 호출하는 사용자.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크:&lt;/b&gt; 클라이언트가 서버에 접속할 수 있게 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시:&lt;/b&gt; 영화 라이브러리 시스템 (비디오 서버, 사진 서버, 웹 서버 등 분리).&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오: 넷플릭스/유튜브 (영상 스트리밍 서비스)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-path-to-node=&quot;16,0,1,0,0,0&quot;&gt;대용량 스트리밍 서비스처럼 &lt;b&gt;대규모 데이터를 제공해야 하는 상황&lt;/b&gt;에서는 클라이언트-서버 아키텍쳐가 유리합니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;16,0,1,0,0,0&quot;&gt;&lt;b&gt;Client (손님):&lt;/b&gt; 내 스마트폰이나 TV 앱입니다. &lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,0,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,0,0,2&quot;&gt;&lt;span&gt;&quot;영화 틀어줘&quot;, &quot;검색해줘&quot;라고 &lt;/span&gt;&lt;b&gt;&lt;span&gt;요청&lt;/span&gt;&lt;/b&gt;&lt;span&gt;만 합니다 &lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,0,0,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,0,0,4&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;16,0,1,1,1,0&quot;&gt;&lt;b&gt;&lt;span&gt;Server 1 (비디오 서버):&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 실제 영화 &lt;/span&gt;&lt;b&gt;&lt;span&gt;영상 파일&lt;/span&gt;&lt;/b&gt;&lt;span&gt;만 전문적으로 스트리밍해 줍니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,1,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,1,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;16,0,1,2,1,0&quot;&gt;&lt;b&gt;&lt;span&gt;Server 2 (회원/추천 서버):&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 내 시청 기록을 분석해서 &quot;좋아할 만한 영화&quot; 목록 데이터를 보내줍니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,2,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,2,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;16,0,1,3,1,0&quot;&gt;&lt;b&gt;&lt;span&gt;Server 3 (이미지 서버):&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 영화 포스터 &lt;/span&gt;&lt;b&gt;&lt;span&gt;썸네일 사진&lt;/span&gt;&lt;/b&gt;&lt;span&gt;만 전송해 줍니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,3,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,0,1,3,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;i&gt;특징:&lt;/i&gt; 영상 서버가 터져도(장애), 로그인이나 영화 목록 검색은 잘 될 수 있습니다(분산 처리).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5) 파이프 앤 필터 아키텍처 (Pipe and Filter)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu (4).png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/stlaK/dJMcahQvwey/x6aTZ5NJgzcrLdVw5AX6U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/stlaK/dJMcahQvwey/x6aTZ5NJgzcrLdVw5AX6U0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/stlaK/dJMcahQvwey/x6aTZ5NJgzcrLdVw5AX6U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FstlaK%2FdJMcahQvwey%2Fx6aTZ5NJgzcrLdVw5AX6U0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2816&quot; height=&quot;1536&quot; data-filename=&quot;Gemini_Generated_Image_n5pu4bn5pu4bn5pu (4).png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력을 받아 처리(Transformation)하고 출력을 내보내는 과정을 연결한 구조입니다35.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징:&lt;/b&gt; 유닉스(UNIX) 쉘 명령어가 대표적입니다. (예: &lt;code&gt;cat log | grep error | wc&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;활용:&lt;/b&gt; 데이터 처리 시스템(Batch processing)에 주로 쓰이며, 즉각적인 반응이 필요한 상호작용(Interactive) 시스템에는 적합하지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시:&lt;/b&gt; 청구서 처리 시스템 (송장 읽기 -&amp;gt; 결제 식별 -&amp;gt; 영수증 발행).&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;시나리오: 인스타그램 사진 필터 / 영수증 처리&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-path-to-node=&quot;20,0,1,0,1,0&quot;&gt;&lt;span&gt;공장 컨베이어 벨트처럼, 입력이 주어지면 가공되어 완성품이 나오는 상황에서 파이프 앤 필터 아키텍쳐가 유리합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;20,0,1,0,1,0&quot;&gt;&lt;b&gt;&lt;span&gt;Input (입력):&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 사용자가 찍은 &lt;/span&gt;&lt;b&gt;&lt;span&gt;원본 사진 데이터&lt;/span&gt;&lt;/b&gt;&lt;span&gt;가 들어옵니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,0,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,0,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;20,0,1,1,1,0&quot;&gt;&lt;b&gt;&lt;span&gt;Filter 1 (보정):&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 사진의 밝기와 대비를 조절하는 필터를 거칩니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,1,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,1,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Filter 2 (효과):&lt;/b&gt; 흑백 효과나 빈티지 효과를 입히는 필터를 거칩니다&lt;span style=&quot;letter-spacing: 0px;&quot; data-path-to-node=&quot;20,0,1,2,1,1&quot;&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot; data-path-to-node=&quot;20,0,1,2,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;20,0,1,3,1,0&quot;&gt;&lt;b&gt;&lt;span&gt;Filter 3 (워터마크):&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 사진 구석에 날짜나 로고를 박는 필터를 거칩니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,3,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,3,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;20,0,1,4,0,0&quot;&gt;&lt;b&gt;Output (출력):&lt;/b&gt; 짠! &lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,4,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,4,0,2&quot;&gt;&lt;span&gt;인스타 감성의 &lt;/span&gt;&lt;b&gt;&lt;span&gt;완성된 사진&lt;/span&gt;&lt;/b&gt;&lt;span&gt;이 나옵니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,4,0,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;20,0,1,4,0,4&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;i&gt;특징:&lt;/i&gt; 중간에 '스티커 붙이기' 단계를 추가하고 싶으면, 파이프 중간에 그 필터만 쏙 끼워 넣으면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처 설계는 시스템의 품질(성능, 보안, 유지보수성 등)을 결정짓는 초기 단계의 핵심 활동입니다. 요구사항에 맞춰 MVC, Layered, Repository 등 적절한 &lt;b&gt;아키텍처 패턴&lt;/b&gt;을 선정하고 적용하는 것이 중요합니다. 다음 포스팅에서는 이를 바탕으로 한 상세 설계와 구현(Design and Implementation)에 대해 다루어 보겠습니다.&lt;/p&gt;</description>
      <category>Computer Science/SW Engineering</category>
      <category>SW Engineering</category>
      <category>SW 공학</category>
      <author>BE_개발자</author>
      <guid isPermaLink="true">https://wondrous-developer.tistory.com/265</guid>
      <comments>https://wondrous-developer.tistory.com/265#entry265comment</comments>
      <pubDate>Wed, 17 Dec 2025 10:03:27 +0900</pubDate>
    </item>
  </channel>
</rss>