유니티와 오픈소스를 바닥부터 파헤치는 기록
유니티를 처음부터 공부하고, AI와 함께 오픈소스 코드를 파헤치며 배운 것을 정리합니다. 오픈소스는 스프링(Spring), Apache HTTP Server(httpd), Tomcat, nginx, MariaDB, Gradle, Kafka를 읽었습니다.
글
-
Gradle 소스 파헤치기 1: AI와 함께 소스를 읽는 방법과 분석 지도
Gradle이 무엇이고 어떻게 동작하며 어디에 쓰는지를 그림과 3분 실습으로 먼저 보고 시작하는, Gradle 소스 읽기 연재의 첫 글입니다. 어떤 판(master, 9.10.0 개발판)을 읽는지, 같은 커밋을 받아 도커에서 Gradle을 Gradle로 빌드하는 법, 100만 줄짜리 소스를 나눈 플랫폼 지도, gradle 명령 한 번이 클라이언트와 데몬을 지나 태스크 액션에 닿는 큰 길, 그리고 AI가 쓴 노트를 소스로 다시 확인하며 읽는 방법을 정리합니다.
-
Gradle 소스 파헤치기 2: 전체 지도 — 클라이언트와 데몬, 서비스 스코프, 빌드의 세 단계
Gradle 소스를 처음 여는 중급 개발자를 위한 지도입니다. gradle 명령 한 번에 움직이는 프로세스 넷, 데몬 안의 상태 계층(프로세스·세션·빌드 트리·빌드·프로젝트)과 그것에 1:1로 맞물린 서비스 스코프, 초기화·설정·실행 세 단계가 어느 클래스의 어떤 상태인지, 모델(Gradle·Settings·Project·Task)과 실행(실행 계획·노드·실행 엔진)의 큰 그림을 실제 소스와 직접 돌린 예제로 확인하고, 3~12편이 이 지도의 어디를 읽는지 정리합니다.
-
Gradle 소스 파헤치기 3: gradle 명령에서 데몬까지 — 런처, 래퍼, 데몬 프로토콜
터미널에서 gradle을 치면 빌드는 그 프로세스가 아니라 뒤에 숨은 데몬 JVM이 합니다. 셸 스크립트가 띄우는 64MiB짜리 클라이언트, 데몬을 쓸지 정하는 세 갈래와 --no-daemon의 진짜 뜻, registry.bin으로 데몬을 찾고 호환성을 따지는 법, 데몬을 띄우고 연결하는 순서, 데몬 안의 명령 사슬 16개와 Idle·Busy 상태, 데몬이 스스로 죽는 조건, 그리고 gradlew와 IDE가 같은 데몬에 닿는 길까지 기준 커밋의 소스와 도커 예제로 확인합니다.
-
Gradle 소스 파헤치기 4: 서비스 레지스트리 — Gradle 안의 DI 컨테이너
태스크 생성자에 @Inject를 붙이면 FileSystemOperations가 들어오는 것은 누구 덕분일까요. 스코프마다 하나씩 만들어 부모를 따라 서비스를 찾는 DefaultServiceRegistry, @Provides 메서드로 서비스를 선언하는 규칙과 장식(decorator), 처음 찾을 때 만드는 게으른 싱글턴, 서비스가 제 스코프에 있는지 보는 ServiceScopeValidator, 모듈 57개가 스코프마다 서비스를 보태는 GradleModuleServices, 그리고 abstract 게터를 채운 _Decorated 클래스까지, 레지스트리를 빌드 밖에서 직접 써 보고 태스크 생성 스택을 찍어 소스와 함께 확인합니다.
-
Gradle 소스 파헤치기 5: 빌드 생명주기 — 초기화, 설정, 실행은 어디서 갈리나
데몬이 받은 빌드 요청이 실행기 사슬을 지나 빌드 생명주기에 닿는 길, 상태 전이를 지키는 StateTransitionController, init 스크립트와 settings를 읽는 초기화, 프로젝트를 설정하는 설정 단계, 설정 시간 장벽과 작업 그래프, 그리고 buildFinished와 Flow API로 끝나는 마무리까지 기준 커밋의 소스로 따라갑니다. 생명주기 훅을 모두 걸어 순서를 찍고, 빌드 오퍼레이션 나무와 세 단계에서 일부러 낸 실패로 확인합니다.
-
Gradle 소스 파헤치기 6: 프로젝트와 빌드 스크립트 — build.gradle.kts가 실행되기까지
build.gradle.kts 한 파일은 소스에서 무엇이 될까요. settings가 적어 둔 설명서로 DefaultProject를 만드는 곳, 프로젝트 잠금 안에서 도는 평가기 사슬과 beforeEvaluate·afterEvaluate 상태, 파일 이름 끝으로 Groovy와 Kotlin을 고르는 선택기, plugins {}를 먼저 떼어 두 번 컴파일하는 두 DSL, tasks.compileJava를 가능하게 하는 생성된 접근자, settings에서 하위 프로젝트로 이어지는 클래스로더 스코프까지, 같은 스크립트를 두 DSL로 돌리고 스택을 찍어 소스와 함께 확인합니다.
-
Gradle 소스 파헤치기 7: 플러그인 적용 — plugins {} 블록에서 apply()까지
plugins { java } 한 줄은 소스에서 무엇이 될까요. 요청을 모두 먼저 해석하고 나서 한꺼번에 적용하는 DefaultPluginRequestApplicator, 핵심 플러그인·buildSrc·포함된 빌드·플러그인 포털을 차례로 묻는 해석기 사슬과 마커 아티팩트, id를 클래스로 바꾸는 META-INF/gradle-plugins 설명 파일, 같은 플러그인을 두 번 걸어도 한 번만 도는 DefaultPluginManager, withPlugin이 불리는 정확한 시점, 확장이 붙는 자리, java → java-base → base 사슬, buildSrc와 precompiled script plugin까지 직접 만든 플러그인으로 확인합니다.
-
Gradle 소스 파헤치기 8: 태스크와 지연 설정 — DefaultTask, TaskContainer, Provider
tasks.register 한 줄은 태스크를 만들지 않습니다. 이름과 설정 블록만 맡겨 둔 TaskCreatingProvider가 언제 실제 태스크가 되는지, configureEach가 지금 있는 태스크와 나중에 올 태스크에 함께 걸리는 원리, withType { }처럼 모르는 새 모든 태스크를 만들어 버리는 API, TaskFactory와 DefaultTask가 액션 목록을 쥐는 법, 그리고 Provider가 값 대신 "값을 만드는 태스크"를 들고 다녀 dependsOn 없이 순서가 생기는 이유와 Property를 굳히는 세 가지 방법까지, 예제 세 개로 소스와 함께 확인합니다.
-
Gradle 소스 파헤치기 9: 태스크 그래프와 실행 계획 — 무엇을, 어떤 순서로, 몇 개씩
gradle pA 의 pA 가 packageApp 이 되고, dependsOn·mustRunAfter·shouldRunAfter·finalizedBy 가 실행 계획의 어떤 관계가 되어 순서를 정하는지, 실행 중에 다음 노드는 누가 어떻게 고르는지, --parallel 과 --max-workers 가 정확히 무엇을 제한하는지, Worker API 가 태스크 안의 일을 어떻게 나눠 돌리는지를 기준 커밋의 소스와 직접 돌린 예제(whenReady, --dry-run, 스레드 이름, jstack)로 확인합니다.
-
Gradle 소스 파헤치기 10: 실행 엔진과 증분 빌드 — UP-TO-DATE는 어떻게 정해지나
콘솔의 UP-TO-DATE 한 단어는 소스에서 어떻게 정해질까요. 태스크를 엔진에 넘기기 전에 거르는 DefaultTaskNodeExecutor, 태스크를 작업 단위로 감싸는 TaskExecution, 스물다섯 개 스텝이 겹겹이 감싼 실행 엔진, 지난 실행 기록과 지금 상태를 비교하는 ResolveChangesStep과 SkipUpToDateStep, @PathSensitive가 바꾸는 파일 지문, 빌드 사이에 파일 상태를 기억하는 VFS, 바뀐 파일만 넘겨주는 InputChanges까지, 직접 돌린 예제와 함께 소스로 확인합니다.
-
Gradle 소스 파헤치기 11: 빌드 캐시와 설정 캐시 — 결과를 다시 쓰는 두 가지 방법
콘솔의 FROM-CACHE와 Reusing configuration cache.는 소스의 어디서 나올까요. 실행 엔진의 스텝 사슬에 끼어 있는 ResolveMutableCachingStateStep과 BuildCacheStep, MD5로 만드는 캐시 키에 무엇이 들어가는지, @CacheableTask와 캐시하지 않는 이유 목록, 키가 파일 이름이 되는 로컬 디렉터리 캐시와 tar.gz 항목, 그리고 작업 그래프를 통째로 직렬화했다가 첫 빌드에서조차 다시 읽어 실행하는 설정 캐시, 환경 변수 읽기를 바이트코드에서 가로채는 핑거프린트와 문제 보고서까지 기준 커밋의 소스와 도커 예제로 확인합니다.
-
Gradle 소스 파헤치기 12: 의존성 해석 — implementation 한 줄이 jar 파일이 되기까지, 그리고 연재를 마치며
build.gradle.kts의 implementation 한 줄은 소스에서 무엇이 될까요. 생성된 접근자가 부르는 DependencyHandler.add(), 선언·해석·소비로 역할이 나뉜 구성(configuration), 파일 목록을 달라고 할 때까지 미뤄지는 해석, DependencyGraphBuilder가 그래프를 넓혀 가는 반복문, 같은 라이브러리의 두 버전이 부딪칠 때 높은 쪽을 고르는 LatestModuleConflictResolver와 strictly·force가 그 결과를 바꾸는 자리, 캐시를 먼저 보고 저장소로 가는 두 번의 탐색과 --offline·읽기 전용 캐시까지 기준 커밋의 소스와 도커 예제로 확인하고, 12편 연재를 정리합니다.
-
Kafka 소스 파헤치기 1: AI와 함께 소스를 읽는 방법과 분석 지도
Kafka가 무엇이고 어떻게 동작하며 어디에 쓰는지를 그림과 3분 실습으로 먼저 보고 시작하는, Kafka 소스 읽기 연재의 첫 글입니다. 어떤 판(trunk, 4.5.0-SNAPSHOT)을 읽는지와 안정판 4.4.0과의 관계, 같은 소스를 받아 도커에서 Gradle로 빌드하는 법과 JSON 스키마에서 코드를 만드는 processMessages 단계, 72만 줄짜리 본체의 모듈 지도, 레코드 하나가 send()에서 디스크와 복제를 거쳐 poll()까지 지나는 큰 길, 그리고 AI가 쓴 노트를 소스로 다시 확인하며 읽는 방법을 정리합니다.
-
Kafka 소스 파헤치기 2: 전체 지도 — 클라이언트, 브로커, 컨트롤러, 그리고 파티션
Kafka 소스를 처음 여는 중급 개발자를 위한 전체 지도입니다. kafka-server-start.sh 가 부르는 kafka.Kafka 의 main 에서 시작해 process.roles 에 따라 BrokerServer·ControllerServer·SharedServer 가 만들어지는 길, 브로커가 시작할 때 만드는 부품, 컨트롤러가 메타데이터 로그로 토픽·파티션·리더를 정하고 브로커에 퍼뜨리는 길, 클라이언트가 Metadata 응답으로 리더를 찾아가는 길, 모듈 의존 방향을 실제 소스와 직접 돌린 예제로 확인하고, 3~12편이 지도의 어디를 확대하는지 짚습니다.
-
Kafka 소스 파헤치기 3: 와이어 프로토콜 — JSON 스키마에서 만들어지는 요청과 응답
프로듀서와 브로커가 TCP 위에서 주고받는 바이트는 [4바이트 길이][요청 헤더][본문] 한 가지 모양입니다. 그 모양을 정하는 JSON 스키마 203개가 빌드 때 자바 클래스 391개가 되는 과정, 판(version)을 고르는 ApiVersions 협상, 옛 판과 새 판이 함께 사는 유연한 판과 태그 필드를 소스로 따라가고, 생성된 클래스만으로 맨 소켓에 요청을 보내는 예제로 바이트를 직접 확인합니다.
-
Kafka 소스 파헤치기 4: 브로커의 네트워크 계층 — Acceptor, Processor, 요청 처리 스레드
브로커의 jstack 에 보이는 socket-acceptor, network-thread, request-handler 세 종류의 스레드가 요청 하나를 어떻게 나눠 맡는지 SocketServer.scala 로 따라갑니다. 리스너마다 Acceptor 하나, Processor 가 바이트를 읽어 요청 큐에 넣고 그 연결을 mute 하면, 요청 처리 스레드가 KafkaApis.handle() 의 match 로 갈래를 나누고, 응답은 요청을 읽은 Processor 로 돌아갑니다. 한 연결의 요청을 하나씩만 처리해 순서를 지키는 mute/unmute, num.network.threads·num.io.threads·queued.max.requests 가 걸리는 자리를 jstack·맨 소켓·요청 로그 예제로 확인하고 Tomcat 의 NIO 커넥터와 비교합니다.
-
Kafka 소스 파헤치기 5: 프로듀서 내부 — send()에서 배치, 그리고 Sender 스레드까지
KafkaProducer.send() 는 네트워크로 아무것도 보내지 않습니다. 레코드를 직렬화하고 파티션을 고른 뒤 RecordAccumulator 의 파티션별 배치에 넣고 Future 를 돌려줄 뿐이고, 실제로 보내는 것은 kafka-producer-network-thread 하나입니다. linger.ms·batch.size 가 걸리는 ready() 의 if, 키 없는 레코드를 한 파티션에 붙여 두는 내장 파티셔너, 배치 메모리를 나눠 주는 BufferPool 과 max.block.ms, 재시도와 delivery.timeout.ms, max.in.flight.requests.per.connection 을 소스로 따라가고, kafka-dump-log.sh 로 배치 수를 세는 예제와 콜백 스레드를 찍는 예제로 확인합니다.
-
Kafka 소스 파헤치기 6: 브로커가 레코드를 받아 쓰기까지 — 레코드 배치 형식과 Produce 요청 처리
프로듀서가 보낸 레코드 배치는 61바이트 헤더와 varint 로 줄인 레코드로 되어 있습니다. 그 바이트를 파일에서 직접 한 칸씩 읽어 보고, Produce 요청 하나가 KafkaApis → ReplicaManager → Partition → UnifiedLog → LogValidator → LogSegment 를 지나 파일에 닿는 길을 소스와 브로커 TRACE 로그로 따라갑니다. 브로커가 배치를 풀지 않고 헤더 몇 칸만 고쳐 그대로 쓰는 설계(CRC 가 그대로인 것을 확인), 토픽의 compression.type 이 다르면 다시 압축하는 경우, acks=0/1/all 이 응답 시점을 바꾸는 자리까지 손으로 만든 Produce 요청으로 확인합니다.
-
Kafka 소스 파헤치기 7: 로그 저장소 — 세그먼트, 인덱스, 보존과 압축
파티션 폴더의 00000000000000003830.log 같은 파일이 언제 새로 생기고, 오프셋 하나를 파일의 바이트 위치로 어떻게 바꾸고, retention.ms 가 지난 세그먼트를 누가 언제 지우고, cleanup.policy=compact 가 같은 키의 옛 값을 어떻게 걷어 내는지 UnifiedLog·LocalLog·LogSegment·OffsetIndex·LogCleaner 소스로 따라갑니다. 세그먼트 롤의 다섯 조건, 4KB마다 한 항목만 적는 희소 인덱스와 mmap, .deleted 로 이름을 바꾼 뒤 지우는 두 단계, 키의 MD5로 만드는 SkimpyOffsetMap, 브로커가 죽었다 살아날 때의 복구 지점을 예제로 확인합니다.
-
Kafka 소스 파헤치기 8: 읽기 경로 — Fetch 요청, 롱 폴링, 그리고 제로 카피
컨슈머의 poll() 이 보낸 Fetch 요청이 브로커 안에서 KafkaApis.handleFetchRequest() → ReplicaManager.fetchMessages() → UnifiedLog.read() → LogSegment.read() 를 지나 파일의 한 조각(FileRecords)이 되고, 데이터가 모자라면 fetch.max.wait.ms 동안 퍼거토리(DelayedFetch)에서 기다렸다가, 마지막에 FileChannel.transferTo() 로 JVM 을 거치지 않고 소켓으로 나가는 길을 Kafka 4.5.0-SNAPSHOT 소스로 따라갑니다. 증분 fetch 세션, 컨슈머와 팔로워가 같은 Fetch 를 쓰는 이유, TLS 에서 제로 카피가 사라지는 이유를 요청 로그·JMX·JFR 예제로 확인합니다.
-
Kafka 소스 파헤치기 9: 복제 — ISR, 하이 워터마크, 그리고 acks=all의 기다림
팔로워 브로커의 ReplicaFetcherThread 가 리더에게 Fetch 를 보내 로그를 따라 쓰고, 리더는 그 Fetch 의 오프셋으로 팔로워의 위치를 알아 하이 워터마크를 올립니다. ISR 에서 빠지는 기준(replica.lag.time.max.ms 와 lastCaughtUpTimeMs), ISR 변경을 컨트롤러에 알리는 AlterPartition, acks=all 요청이 DelayedProduce 로 퍼거토리에서 기다리는 조건과 min.insync.replicas 가 거절하는 자리, 리더가 바뀐 뒤 리더 에포크로 갈라진 로그를 잘라 내는 길을 Partition.scala·AbstractFetcherThread.scala 로 따라가고, 브로커 셋을 멈추고 죽이는 예제로 확인합니다.
-
Kafka 소스 파헤치기 10: KRaft와 메타데이터 — 컨트롤러는 클러스터를 어떻게 기억하나
토픽·파티션·리더·브로커 목록 같은 클러스터의 장부가 __cluster_metadata 라는 평범한 Kafka 로그에 레코드로 쌓이고, 컨트롤러들이 Raft 로 그 로그를 함께 지키는 과정을 소스로 따라갑니다. KafkaRaftClient 의 역할 상태(Prospective·Candidate·Leader·Follower)와 Vote·Fetch, 투표자 과반으로 정하는 하이 워터마크, 이벤트 스레드 하나로 도는 QuorumController 가 레코드를 만들고 커밋 전에 메모리에 먼저 적용했다가 되돌리는 SnapshotRegistry, 브로커가 레코드를 MetadataDelta·MetadataImage 로 바꿔 적용하는 MetadataLoader, 하트비트와 펜스를 kafka-dump-log·kafka-metadata-quorum·컨트롤러 3대 선거 예제로 확인합니다.
-
Kafka 소스 파헤치기 11: 컨슈머와 그룹 코디네이터 — poll()과 리밸런스의 안쪽
KafkaConsumer 는 group.protocol 값에 따라 ClassicKafkaConsumer 나 AsyncKafkaConsumer 에 일을 넘기는 껍데기입니다. 기본값 classic 에서는 poll() 을 부른 스레드가 JoinGroup·SyncGroup 으로 리밸런스를 직접 치르고 리더 컨슈머가 할당을 계산하며, consumer 프로토콜(KIP-848)에서는 백그라운드 스레드가 ConsumerGroupHeartbeat 하나로 브로커와 이야기하고 브로커가 할당을 정합니다. 브로커 쪽 그룹 코디네이터가 그룹 상태를 __consumer_offsets 에 레코드로 쓰고 다시 읽어 되살리는 CoordinatorRuntime 까지, 컨슈머 두 개를 차례로 붙이는 예제와 kafka-dump-log.sh 로 확인합니다.
-
Kafka 소스 파헤치기 12: 멱등성과 트랜잭션 — 정확히 한 번은 어떻게 만들어지나, 그리고 연재를 마치며
enable.idempotence=true 와 transactional.id 한 줄 뒤에서 무슨 일이 생기는지 소스로 따라갑니다. 브로커가 producer id·에포크·시퀀스로 재시도 중복을 가려내는 곳, 트랜잭션 코디네이터가 __transaction_state 에 상태를 적고 커밋·중단 마커(컨트롤 레코드)를 파티션마다 쓰게 하는 길, read_committed 컨슈머가 마지막 안정 오프셋(LSO)과 중단된 트랜잭션 목록으로 걸러 읽는 방법, 같은 transactional.id 의 옛 프로듀서를 에포크로 막는 펜싱을 맨 소켓·kafka-dump-log·kafka-transactions 예제로 확인합니다. 마지막 글이라 1~12편을 되짚으며 연재를 마칩니다.
-
MariaDB 소스 파헤치기 1: AI와 함께 소스를 읽는 방법과 분석 지도
MariaDB가 무엇이고 어떻게 동작하며 어디에 쓰는지를 그림과 3분 실습으로 먼저 보고 시작하는, MariaDB 서버 소스 읽기 연재의 첫 글입니다. 어떤 판(main 브랜치, 13.2.0 alpha)을 읽는지와 LTS 판과의 차이, 서브모듈까지 같은 소스를 받아 도커에서 Debug 빌드하는 법(Windows에서 CRLF 때문에 빌드가 깨진 이유 포함), 259만 줄짜리 소스의 폴더 지도, 쿼리 하나가 연결 스레드에서 InnoDB까지 지나는 큰 길을 gdb 백트레이스로 확인한 결과, 그리고 AI가 쓴 노트를 소스로 다시 확인하며 읽는 방법을 정리합니다.
-
MariaDB 소스 파헤치기 2: 전체 지도 — 서버 계층과 스토리지 엔진, 그리고 쿼리 하나의 일생
MariaDB 소스를 처음 여는 중급 개발자를 위한 연재의 지도입니다. 서버 계층(sql/)과 스토리지 엔진(storage/)을 왜 나눴는지, 둘의 경계인 handler(테이블을 연 객체)와 handlerton(엔진마다 하나인 함수 포인터 표)이 어떻게 생겼는지, SELECT 하나가 연결 → THD → 파싱 → 준비 → 최적화 → 실행 → handler → 엔진을 지나 Protocol 로 돌아오는 길, 그리고 그 모든 객체를 문장마다 한꺼번에 치우는 MEM_ROOT 를 실제 소스와 gdb 백트레이스로 확인합니다.
-
MariaDB 소스 파헤치기 3: 기동과 플러그인 — mariadbd가 첫 연결을 받기까지
mariadbd 를 켜면 첫 연결을 받기 전까지 무슨 일이 일어나는지 sql/mysqld.cc 의 mysqld_main() 을 따라 읽습니다. my.cnf 한 줄이 시스템 변수가 되는 길, InnoDB·Aria 같은 엔진까지 전부 플러그인으로 등록하고 초기화하는 plugin_init(), 엔진이 handlerton 을 채우는 ha_initialize_handlerton(), 신호 스레드와 poll() 루프, 그리고 종료가 기동의 거꾸로 일어나는 모습을 오류 로그와 gdb 로 확인합니다.
-
MariaDB 소스 파헤치기 4: 연결과 스레드 — 연결마다 스레드 하나, 그리고 스레드 풀
mariadbd 의 메인 스레드가 poll() 로 접속을 받아 CONNECT 를 만들고, thread_handling 이 고른 스케줄러(함수 포인터 묶음)에 넘기기까지를 소스로 따라갑니다. 연결마다 스레드 하나를 두는 기본 모드와 스레드 캐시, THD 를 만들고 인증하는 로그인 단계, 그리고 스레드 그룹·리스너·워커·timer_thread 로 이루어진 스레드 풀이 놀고 있는 연결을 스레드에서 떼어 내는 방법을 ps·information_schema·gdb 실행 결과로 확인하고, httpd 의 MPM·Tomcat 의 엔드포인트와 비교합니다.
-
MariaDB 소스 파헤치기 5: 프로토콜과 명령 처리 — 패킷 하나가 dispatch_command에 닿기까지
클라이언트가 보낸 바이트가 어떻게 패킷이 되고, 첫 바이트 하나로 COM_QUERY·COM_PING·COM_STMT_EXECUTE가 갈리고, 결과가 다시 패킷이 되어 나가는지를 sql/net_serv.cc, sql_parse.cc의 do_command()·dispatch_command(), protocol.cc로 따라갑니다. bash의 /dev/tcp로 클라이언트 라이브러리 없이 서버와 직접 말을 주고받고, 쿼리 캐시가 왜 파싱보다 먼저 문자열로 찾는지도 실행해서 확인합니다.
-
MariaDB 소스 파헤치기 6: 파서 — SQL 문장이 LEX와 Item 나무가 되기까지
문자열로 들어온 SQL 한 문장이 렉서(Lex_input_stream)에서 토큰이 되고, Bison이 만든 파서(MYSQLparse·ORAparse)의 규칙 액션에서 LEX·SELECT_LEX·TABLE_LIST·Item 나무가 되는 과정을 소스로 따라갑니다. 문법 파일 하나에서 파서 두 개가 생기는 빌드 규칙, 키워드 표와 state_map, "near '...' at line N" 오류가 만들어지는 곳을 보고, Bison 추적과 gdb로 파서가 남긴 나무를 직접 찍어 봅니다.
-
MariaDB 소스 파헤치기 7: 준비 단계 — 권한 확인, 테이블 열기, 이름 찾기
파서가 만든 LEX를 받은 서버가 실행 계획을 세우기 전에 하는 세 가지 일을 소스로 따라갑니다. mysql_execute_command()의 큰 switch에서 check_table_access()로 권한을 보고, open_and_lock_tables()로 메타데이터 잠금을 얻고 TABLE_SHARE와 TABLE을 캐시에서 꺼내고, JOIN::prepare()에서 열 이름을 Field에 잇습니다. 열 권한 오류 1142·1143, Opened_tables와 Table_open_cache_hits의 변화, ALTER TABLE이 열린 트랜잭션 뒤에서 기다리고 그 뒤 SELECT까지 줄을 서는 모습, 1054·1052 오류가 나는 호출 스택을 직접 확인합니다.
-
MariaDB 소스 파헤치기 8: 옵티마이저 — JOIN::optimize와 실행 계획 고르기
EXPLAIN 한 줄이 만들어지기까지 MariaDB 옵티마이저가 하는 일을 소스로 따라갑니다. JOIN::optimize()의 단계(조건 다듬기, 행 수 추정, 범위 분석, 조인 순서 탐색), 1 = 1밀리초로 정한 새 비용 모델, 히스토그램이 계획을 뒤집는 과정, EXPLAIN과 optimizer_trace가 만들어지는 곳을 예제와 gdb로 확인합니다.
-
MariaDB 소스 파헤치기 9: 실행과 handler API — 중첩 루프 조인이 엔진을 부르는 법
옵티마이저가 고른 계획이 실제로 행을 읽어 내는 곳을 소스로 따라갑니다. JOIN::exec()에서 do_select()와 sub_select()·evaluate_join_record()의 중첩 루프로, EXPLAIN의 type마다 정해지는 읽기 함수로, 조인 버퍼로, handler의 ha_ 감싸개를 지나 ha_innobase::rnd_next()와 write_row()까지 내려갑니다. Handler_read_* 상태 변수로 호출 수를 세고, gdb 백트레이스 한 장으로 do_command부터 엔진까지를 확인하고, 엔진을 MEMORY·Aria·BLACKHOLE로 바꿔도 같은 자리를 지나는 것을 봅니다.
-
MariaDB 소스 파헤치기 10: InnoDB 1 — 페이지, 버퍼 풀, B-트리
handler 아래로 내려가 InnoDB가 데이터를 담는 세 가지 그릇을 소스로 읽습니다. 16KB 페이지의 FIL 헤더·페이지 헤더·레코드·페이지 디렉터리, 페이지를 메모리에 붙잡아 두는 버퍼 풀(buf_page_get_gen, LRU의 old 구역, 더티 페이지와 page cleaner), 테이블 그 자체인 클러스터드 인덱스 B-트리(search_leaf, 페이지 분할), 그리고 페이지 잠금과 redo를 묶는 미니 트랜잭션 mtr_t입니다. .ibd 파일을 od로 열어 페이지 머리를 읽고, information_schema로 버퍼 풀 안을 보고, 행을 늘려 B-트리 높이와 분할을 세고, gdb로 SELECT·INSERT가 B-트리에 닿는 길을 확인합니다.
-
MariaDB 소스 파헤치기 11: InnoDB 2 — 트랜잭션, MVCC, undo, 잠금
COMMIT 한 줄이 서버 계층의 ha_commit_trans()에서 InnoDB의 trx_t::commit()까지 가는 길, REPEATABLE READ가 옛 값을 보여 주는 ReadView와 undo 사슬, 아무도 안 보는 옛 판을 치우는 purge, 그리고 행 잠금·갭 잠금·잠금 대기·교착 탐지를 기준 커밋의 소스로 따라갑니다. 두 세션 예제로 스냅샷, 잠금 대기, 교착, history list length를 직접 확인합니다.
-
MariaDB 소스 파헤치기 12: 로그 두 개 — redo 로그와 binlog, 크래시 복구, 그리고 연재를 마치며
COMMIT이 돌아왔을 때 무엇이 디스크에 있다는 뜻인지, InnoDB의 redo 로그(log_sys, LSN, mtr_t::commit, log_write_up_to)와 innodb_flush_log_at_trx_commit이 갈리는 자리, 체크포인트와 ib_logfile0의 배치, kill -9 뒤 redo로 앞으로 가고 undo로 되돌아오는 크래시 복구, 서버 계층의 binlog와 이벤트, 두 로그를 XID로 맞추는 2단계 커밋과 그룹 커밋을 소스로 따라갑니다. DBUG 크래시 지점으로 2단계 커밋의 두 틈에서 서버를 죽여 복구가 무엇을 하는지 직접 확인하고, 연재 열두 편을 정리합니다.
-
nginx 소스 파헤치기 1: AI와 함께 소스를 읽는 방법과 분석 지도
nginx가 무엇이고 어떻게 동작하며 어디에 쓰는지를 그림과 3분 실습으로 먼저 보고 시작하는, nginx 소스 읽기 연재의 첫 글입니다. 어떤 판(mainline 1.31.9 개발판)을 읽는지와 stable 1.30.x와의 차이, 같은 소스를 받아 도커에서 auto/configure와 make로 빌드하는 법, 26만 줄짜리 소스의 폴더 지도, 요청 하나가 accept에서 sendfile까지 지나는 큰 길을 gdb와 strace로 확인한 결과, 그리고 AI가 쓴 노트를 소스로 다시 확인하며 읽는 방법을 정리합니다.
-
nginx 소스 파헤치기 2: 전체 지도 — 마스터와 워커, 모듈, cycle, 설정 문맥
nginx 소스를 처음 여는 중급 프로그래머를 위한 연재 전체의 지도입니다. 마스터 하나와 워커 여럿이 각각 어디서 기다리는지, 모든 기능이 ngx_module_t 하나로 꽂히는 모듈 구조와 "CORE"·"HTTP" 글자로 된 모듈 종류, 실행 중인 nginx의 전부를 담은 ngx_cycle_t와 conf_ctx, nginx.conf의 main·events·http·server·location 문맥이 비트 플래그로 검사되는 방법, 한 워커의 이벤트 루프가 수백 개의 연결을 도는 모습을 실제 소스와 ps·gdb 예제로 확인합니다.
-
nginx 소스 파헤치기 3: 메모리 풀과 기본 자료구조 — ngx_pool_t, ngx_str_t, ngx_buf_t와 ngx_chain_t
nginx 의 C 코드에도 free() 는 거의 없습니다. 요청 하나, 연결 하나처럼 수명이 같은 메모리를 풀에 모아 한꺼번에 버리기 때문입니다. ngx_pool_t 의 블록 사슬과 failed 카운트, 큰 할당을 따로 다루는 large 목록, cleanup, NUL 로 끝나지 않는 ngx_str_t, 배열·리스트·큐·해시, 그리고 응답을 담는 ngx_buf_t 와 ngx_chain_t 를 실제 소스와 디버그 로그·gdb 로 확인합니다.
-
nginx 소스 파헤치기 4: 기동과 설정 읽기 — main()에서 ngx_init_cycle까지
nginx 를 켜면 main() 이 명령줄을 읽고, ngx_init_cycle() 이 nginx.conf 를 읽어 cycle 이라는 설정 한 벌을 만든 뒤에야 마스터 프로세스가 돌기 시작합니다. 설정 파서가 파일·블록·명령줄(-g) 세 경우를 한 함수로 처리하는 법, 한 글자씩 읽어 낱말로 나누는 ngx_conf_read_token(), 오류 메시지가 찍히는 자리, reload 가 같은 ngx_init_cycle() 을 다시 부르며 옛 cycle 에서 듣기 소켓과 공유 메모리를 물려받는 과정을 nginx 1.31.9 소스와 gdb·strace 예제로 따라갑니다.
-
nginx 소스 파헤치기 5: 모듈과 디렉티브 — nginx.conf 한 줄이 모듈의 설정 구조체가 되기까지
nginx 의 모듈은 ngx_module_t 구조체 하나이고, 디렉티브는 그 구조체가 가리키는 ngx_command_t 표의 한 줄입니다. configure 가 만드는 모듈 배열, load_module 로 읽는 동적 모듈, 디렉티브 이름으로 함수를 찾는 ngx_conf_handler(), http·server·location 마다 모듈별 설정 구조체를 만드는 ngx_http_block(), 그리고 설정 때 한 번에 끝나는 병합(merge)을 소스로 따라갑니다. 디렉티브 넷을 가진 동적 모듈을 직접 만들어 콜백 순서와 병합 결과, 오류 메시지를 확인합니다.
-
nginx 소스 파헤치기 6: 마스터와 워커 — 프로세스, 시그널, 무중단 reload
nginx -s reload 한 줄이 어떻게 마스터의 sigsuspend() 루프를 깨우고, 새 설정으로 새 워커를 띄운 뒤 옛 워커를 하던 일을 마치게 하고 내보내는지 src/os/unix/ngx_process_cycle.c 를 따라 읽습니다. 시그널 핸들러가 플래그만 세우는 이유, 마스터와 워커를 잇는 socketpair 채널, ngx_processes[] 표의 respawn·just_spawn·exiting 비트, kill -9 당한 워커를 다시 띄우는 길, USR2 바이너리 교체까지 strace·디버그 로그·ps 로 확인합니다.
-
nginx 소스 파헤치기 7: 이벤트 루프 — epoll, 타이머, posted 이벤트, accept
워커 하나가 스레드 하나로 수천 연결을 돌리는 ngx_process_events_and_timers() 한 바퀴를 소스로 따라갑니다. epoll 을 함수 포인터 표(ngx_event_actions)로 끼우는 법, worker_connections 만큼 미리 만든 연결 배열과 빈 목록, EPOLLET 와 낡은 이벤트를 거르는 instance 비트, 레드블랙 트리 타이머가 epoll_wait 의 타임아웃이 되는 길, posted 큐, 그리고 accept_mutex(기본 off)·EPOLLEXCLUSIVE·reuseport 가운데 누가 새 연결을 받는지를 strace·gdb·디버그 로그로 확인합니다.
-
nginx 소스 파헤치기 8: HTTP 요청 읽기 — 연결이 ngx_http_request_t가 되기까지
accept한 소켓 하나가 nginx 안에서 ngx_http_request_t가 되기까지를 소스로 따라갑니다. 데이터가 올 때까지 요청 구조체를 만들지 않는 이유, switch 상태 기계로 된 요청 줄·헤더 파서, client_header_buffer_size와 large_client_header_buffers가 쓰이는 자리, Host 헤더로 server 블록을 고르는 순서, keep-alive와 파이프라인을 디버그 로그·gdb·nc로 직접 확인합니다.
-
nginx 소스 파헤치기 9: 11단계 phase와 location 찾기 — 요청은 어떤 순서로 처리되나
헤더를 다 읽은 요청은 nginx 안에서 11개의 phase를 차례로 지납니다. 모듈들이 등록한 핸들러가 설정 때 한 줄짜리 ph[] 배열로 펴지고, ngx_http_core_run_phases()가 그 배열을 checker 함수로 돌리는 구조를 소스에서 확인합니다. location 블록이 접두사 트리로 바뀌어 =, ^~, 정규식, 1.31.x의 predicate location 순서로 골라지는 과정과 rewrite last·break, 내부 리다이렉트, named location이 배열의 어느 칸으로 되돌아가는지도 디버그 로그와 gdb로 직접 봅니다.
-
nginx 소스 파헤치기 10: 필터와 정적 파일 — 응답은 어떻게 흘러 나가나
정적 파일 하나가 응답이 되어 소켓까지 가는 길을 nginx 1.31.9 소스로 따라갑니다. 파일 내용을 읽지 않고 ngx_buf_t 에 "파일의 몇 번째부터 몇 번째"만 적는 static 핸들러, 함수 포인터 두 개로 이어 붙인 헤더·본문 필터 사슬과 그 순서를 정하는 ngx_modules.c, 파일을 메모리로 읽을지 정하는 copy 필터와 ngx_output_chain, 모아 두었다 보내는 write 필터와 writev + sendfile 까지 gdb·strace·디버그 로그로 확인합니다.
-
nginx 소스 파헤치기 11: upstream과 proxy — 리버스 프록시의 한 요청
proxy_pass 한 줄이 location 에 콘텐츠 핸들러를 걸고, proxy 모듈이 ngx_http_upstream_t 의 콜백을 채우면 upstream 이 논블로킹 connect·요청 보내기·응답 헤더 읽기·본문 흘려보내기를 맡는 과정을 소스로 따라갑니다. 백엔드로 실제 나가는 요청(이 판의 기본값 HTTP/1.1, 보내지 않는 Connection 헤더), 502·504 가 정해지는 자리와 proxy_next_upstream, proxy_buffering on/off 와 임시 파일을 디버그 로그·gdb·작은 백엔드로 확인합니다.
-
nginx 소스 파헤치기 12: 부하 분산, upstream keepalive, stream 모듈 — 그리고 연재를 마치며
upstream {} 에 서버를 여러 대 적으면 nginx는 요청마다 어느 서버로 보낼지를 어떻게 고를까요. peer.init·get·free 세 함수 포인터로 짜인 부하 분산의 틀, 가중치 5:1:1을 a a b a c a a로 고르게 펴는 smooth weighted round robin, max_fails와 fail_timeout, 그 get/free를 감싸 연결을 캐시에 넣었다 꺼내는 keepalive 모듈(이 판에서는 적지 않아도 켜집니다), 그리고 같은 바닥을 TCP에 쓴 stream 모듈을 소스와 도커 예제로 확인하고 연재를 마무리합니다.
-
Apache httpd 소스 파헤치기 1: AI와 함께 소스를 읽는 방법과 분석 지도
Apache httpd가 무엇이고 어떻게 동작하며 어디에 쓰는지를 그림과 3분 실습으로 먼저 보고 시작하는, httpd 소스 읽기 연재의 첫 글입니다. 어떤 판(trunk 2.5.1-dev)을 읽는지, 같은 소스를 받아 도커에서 빌드하는 법과 실제로 막혔던 곳, 35만 줄짜리 소스의 폴더 지도, 요청 하나가 지나는 큰 길, 그리고 AI가 쓴 노트를 소스로 다시 확인하며 읽는 방법을 정리합니다.
-
Apache httpd 소스 파헤치기 2: C로 짠 서버를 읽기 전에 — 구조체, 함수 포인터, 매크로
C의 구조체가 C++나 Java의 객체보다 아름답다는 말은 어디서 나왔을까요. 클래스도 인터페이스도 없는 C로 httpd가 모듈을 끼우는 방법을 실제 소스와 실행 결과로 확인합니다. 숨은 것 없는 메모리 모양, 함수 포인터로 만든 인터페이스, void * 배열과 module_index, 매크로로 찍어 내는 훅, 따로 컴파일한 .so를 믿는 MMN 검사, 그리고 C가 대신 해 주지 않는 것들까지.
-
Apache httpd 소스 파헤치기 3: 전체 지도 — 코어, APR, MPM, 모듈과 세 개의 큰 구조체
Apache httpd 소스를 처음 여는 사람을 위한 지도입니다. 운영체제·APR·코어·MPM·모듈로 나뉘는 층, 코어조차 모듈이라는 것, 실행 파일에 묶이는 모듈과 LoadModule로 싣는 모듈의 차이, server_rec·conn_rec·request_rec 세 구조체와 풀 계층, 요청 하나가 지나가는 길을 실제 소스와 직접 돌린 예제로 확인합니다.
-
Apache httpd 소스 파헤치기 4: APR 메모리 풀 — free 없이 메모리를 관리하는 법
httpd 의 C 코드에는 free() 가 거의 보이지 않습니다. 메모리를 하나하나 돌려주는 대신 "요청 하나", "연결 하나" 처럼 수명이 같은 것끼리 풀에 묶어 한꺼번에 버리기 때문입니다. APR 풀의 노드와 allocator, 포인터 하나를 미는 것으로 끝나는 apr_palloc, cleanup 의 실행 순서, httpd 가 풀을 만들고 지우는 자리, 그리고 흔한 실수 두 가지를 실제 소스와 실행 결과로 확인합니다.
-
Apache httpd 소스 파헤치기 5: main()과 기동 — 설정을 두 번 읽는 이유와 재시작
httpd를 켜면 main() 안에서 무슨 일이 일어나는지 server/main.c 914줄을 따라 읽습니다. 설정 파일을 왜 두 번 읽는지, apachectl graceful 한 줄이 어떻게 main()의 루프를 한 바퀴 더 돌게 하는지, 루트로 시작한 서버가 어디서 User로 내려가는지를 소스와 실제 실행 로그로 확인합니다.
-
Apache httpd 소스 파헤치기 6: 모듈과 훅 — 모듈 하나가 서버에 끼어드는 방법
httpd 의 기능은 거의 전부 모듈이고, 모듈은 훅이라는 정해진 자리에 함수를 걸어 서버에 끼어듭니다. module 구조체, 모듈이 등록되는 길(정적 모듈과 LoadModule), 훅을 만드는 매크로가 실제로 무엇으로 펼쳐지는지, RUN_FIRST 와 RUN_ALL 의 차이, 순서 값과 앞뒤 모듈 이름으로 정하는 실행 순서를 기준 커밋의 소스와 직접 만든 모듈 세 개로 확인합니다.
-
Apache httpd 소스 파헤치기 7: 설정 파일과 디렉티브 — httpd.conf 한 줄이 C 구조체가 되기까지
httpd.conf 에 적은 한 줄이 어떻게 모듈의 C 함수를 부르고 구조체에 값을 넣는지 server/config.c 를 따라갑니다. 파일을 트리로 읽는 단계와 그 트리를 걷는 단계, 이름으로 디렉티브를 찾는 해시, TAKE1·FLAG·ITERATE 같은 인자 방식, "not allowed here" 가 나오는 비트 검사와 AllowOverride, 그리고 Directory·Location·VirtualHost 섹션의 값이 언제 병합되는지를 직접 만든 디렉티브로 확인합니다.
-
Apache httpd 소스 파헤치기 8: MPM — prefork, worker, event는 연결을 어떻게 나눠 맡나
httpd.conf 의 LoadModule mpm_event_module 한 줄이 무엇을 바꾸는지 소스로 따라갑니다. MPM 이 모듈 하나로 갈아 끼워지는 원리(mpm 훅과 ap_mpm_query), 프로세스만 쓰는 prefork, 리스너 스레드와 큐로 일을 나누는 worker, keep-alive 연결을 스레드에서 떼어 epoll 에 맡기는 event, 1초마다 자식을 늘리고 줄이는 부모의 루프와 공유 메모리 스코어보드, 그리고 StartServers·MaxRequestWorkers·ThreadsPerChild 가 코드의 어디서 쓰이는지를 ps·mod_status·gdb 실행 결과로 확인합니다.
-
Apache httpd 소스 파헤치기 9: 연결과 요청 읽기 — 소켓 하나가 request_rec가 되기까지
MPM이 받아 넘긴 소켓 하나가 conn_rec가 되고, 요청 줄과 헤더를 읽어 request_rec가 되기까지를 httpd 소스로 따라갑니다. 코어가 훅의 맨 끝에 서는 이유, keep-alive 루프가 MPM에 따라 어디에 있는지, trunk에서 요청 줄을 읽는 일이 필터로 옮겨 간 것, 그리고 Host 헤더로 가상 호스트를 고르는 두 단계를 예제와 함께 확인합니다.
-
Apache httpd 소스 파헤치기 10: 요청 처리 단계 — URL에서 파일과 핸들러까지
GET /hello.html 한 줄이 httpd 안에서 어떤 단계를 거쳐 디스크의 파일과 그것을 내보낼 핸들러로 바뀌는지, ap_process_request_internal()의 단계 순서를 따라 소스에서 확인합니다. Location·Directory·Files 설정이 합쳐지는 세 가지 걷기, mod_dir의 서브요청, ErrorDocument의 내부 리다이렉트도 모든 단계에 로그를 찍는 모듈로 직접 봅니다.
-
Apache httpd 소스 파헤치기 11: 필터와 버킷 브리게이드 — 응답은 어떻게 흘러 나가나
핸들러가 만든 응답이 소켓에 닿기까지 httpd 안에서 어떤 모양으로 흐르는지, 소스를 처음 여는 사람 기준으로 따라갑니다. APR-util 의 버킷과 브리게이드(링), 읽을 때 모양이 바뀌는 PIPE·FILE 버킷, 여섯 단계로 줄 서는 필터와 ap_pass_brigade, chunked 인코딩과 sendfile, 그리고 소켓이 가득 찼을 때 코어 출력 필터가 하는 일을 직접 쓴 필터 모듈의 로그로 확인합니다.
-
Apache httpd 소스 파헤치기 12: 핸들러와 정적 파일 — 파일 하나는 어떻게 응답이 되나
SetHandler와 AddHandler가 정하는 "핸들러"가 Content-Type과 어떻게 다른지, 아무도 맡지 않은 요청을 코어의 default_handler()가 어떻게 파일 버킷 하나로 내보내는지를 소스로 따라갑니다. ETag와 304, Range와 206, 끝 슬래시 301과 DirectoryIndex, 그리고 EnableSendfile·EnableMMAP이 실제로 어떤 시스템 콜을 바꾸는지도 curl과 strace로 확인합니다.
-
Apache httpd 소스 파헤치기 13: 인증과 권한 — Require 한 줄은 어떻게 판정되나
httpd.conf 의 AuthType Basic, AuthUserFile, Require valid-user, <RequireAll> 이 소스에서 어떻게 움직이는지 따라갑니다. "누구인가"를 묻는 인증과 "들어와도 되는가"를 묻는 권한이 왜 다른 모듈로 나뉘었는지, 이름으로 갈아 끼우는 프로바이더, Require 트리와 GRANTED·DENIED·NEUTRAL 판정, 그리고 같은 판정을 두 번 돌리는 request.c 의 순서를 실제 소스와 예제 출력으로 확인합니다.
-
Apache httpd 소스 파헤치기 14: mod_proxy — 리버스 프록시와 로드 밸런서
ProxyPass 한 줄이 요청을 다른 서버로 넘기기까지를 소스로 따라갑니다. URL이 r->filename의 "proxy:..."로 바뀌는 자리, proxy_handler()가 스킴 핸들러 훅에 일을 넘기는 방식, 백엔드마다 하나씩 생기는 워커와 연결 풀, mod_proxy_http가 브리게이드로 응답을 흘려보내는 길, 그리고 byrequests 밸런서가 두 백엔드를 번갈아 고르는 계산을 컨테이너 안의 백엔드 두 대로 직접 확인합니다.
-
Apache httpd 소스 파헤치기 15: mod_ssl — TLS는 어디에 끼어드나
httpd 코어는 TLS를 모릅니다. 그런데도 SSLEngine on 한 줄이면 HTTPS가 됩니다. mod_ssl이 훅으로 기동과 연결에 끼어들고, 연결 필터와 직접 만든 BIO로 OpenSSL을 필터 사슬 사이에 끼우고, SNI로 가상 호스트와 인증서를 바꾸고, SSL_* 변수를 다른 모듈에 내주는 과정을 소스와 예제로 따라갑니다.
-
Apache httpd 소스 파헤치기 16: 로그 — 접근 로그와 오류 로그, 그리고 연재를 마치며
httpd의 두 가지 로그가 소스에서 어떻게 남는지 따라갑니다. ap_log_rerror() 한 줄이 매크로로 레벨을 거르는 방법, LogLevel을 모듈마다 다르게 거는 module_index, ErrorLogFormat과 LogFormat 문자열이 함수 포인터 배열이 되는 과정, 요청이 끝난 뒤 log_transaction 훅이 불리는 때, 파이프 로그를 다시 띄우는 구조를 예제로 확인하고, 16편 연재를 정리합니다.
-
Tomcat 소스 파헤치기 1: AI와 함께 소스를 읽는 방법과 분석 지도
Tomcat이 무엇이고 어떻게 동작하며 어디에 쓰는지를 그림과 3분 실습으로 먼저 보고 시작하는, Tomcat 소스 읽기 연재의 첫 글입니다. 어떤 판(main 브랜치, 12.0.0-M1)을 읽는지와 안정판 11.0.x와의 차이, 서블릿 스펙 버전, 같은 소스를 받아 도커에서 Ant로 빌드하는 법, 74만 줄짜리 소스의 폴더 지도, 요청 하나가 Acceptor에서 서블릿까지 지나는 큰 길, 그리고 AI가 쓴 노트를 소스로 다시 확인하며 읽는 방법을 정리합니다.
-
Tomcat 소스 파헤치기 2: 전체 지도 — Server, Service, Connector, 그리고 컨테이너 넷
Tomcat 소스를 처음 여는 사람을 위한 지도입니다. server.xml의 Server·Service·Connector·Engine·Host가 각각 어떤 클래스가 되는지, Engine·Host·Context·Wrapper 네 컨테이너가 부모·자식으로 어떻게 이어지는지, 커넥터(Coyote)와 서블릿 컨테이너(Catalina)를 왜 나눴고 Adapter 하나로 어떻게 잇는지, Jasper·EL·JULI·Naming은 어디에 있는지를 실제 소스와 직접 돌린 예제로 확인합니다.
-
Tomcat 소스 파헤치기 3: Lifecycle과 기동 — catalina.sh start 뒤에 일어나는 일
catalina.sh start 한 줄 뒤에서 Bootstrap이 클래스로더를 만들고, Catalina가 Digester로 server.xml을 객체 나무로 바꾸고, 모든 부품이 같은 Lifecycle 상태 기계를 따라 init·start되는 과정을 소스로 따라갑니다. 부모가 자식을 어떤 순서로 시작시키는지, 커넥터가 실패해도 서버가 뜨는 이유, 종료 포트 8005가 하는 일을 리스너와 예제로 직접 확인합니다.
-
Tomcat 소스 파헤치기 4: 클래스로더 — 웹앱마다 따로 보는 클래스
Tomcat 한 프로세스 안에서 두 웹앱이 같은 이름의 서로 다른 클래스를 쓸 수 있는 이유를 소스에서 찾습니다. Bootstrap 이 만드는 common·server·shared 로더와 catalina.properties, 웹앱마다 하나씩 생기는 WebappClassLoaderBase 가 부모 위임 순서를 뒤집는 loadClass(), jakarta.servlet 같은 예외를 고르는 filter(), delegate 설정, reloadable 리로드와 스레드 누수 경고까지 기준 커밋을 빌드한 Tomcat 으로 직접 확인합니다.
-
Tomcat 소스 파헤치기 5: 커넥터와 NIO 엔드포인트 — 연결은 어떤 스레드가 받나
server.xml 의 <Connector port="8080"> 한 줄이 Connector, Http11NioProtocol, NioEndpoint 라는 세 객체가 되고, 그 안에서 Acceptor·Poller·작업 스레드가 연결 하나를 나눠 맡는 과정을 소스로 따라갑니다. keep-alive 연결이 요청 사이에 스레드를 놓아주는 길, JDK 와 다르게 동작하는 Tomcat 의 스레드 풀(TaskQueue), maxConnections·acceptCount·maxThreads 가 걸리는 자리를 jstack 과 소켓 예제로 확인하고, main 브랜치에서 NIO2 커넥터가 빠진 것도 11.0.x 와 비교합니다.
-
Tomcat 소스 파헤치기 6: HTTP/1.1 요청 읽기 — 바이트가 coyote Request가 되기까지
소켓에서 읽은 바이트가 Http11Processor의 service() 루프 안에서 요청 줄과 헤더로 나뉘어 coyote Request가 되는 과정을 소스로 따라갑니다. 문자열을 늦게 만드는 MessageBytes, maxHttpHeaderSize와 maxHeaderCount 한도, 잘못된 요청이 HTML 400 페이지가 되는 길, keep-alive와 chunked 응답이 정해지는 자리를 날 요청을 보내 직접 확인합니다.
-
Tomcat 소스 파헤치기 7: CoyoteAdapter와 Mapper — 요청은 어느 서블릿으로 가나
다 읽은 HTTP 요청이 Coyote에서 Catalina로 넘어가는 다리인 CoyoteAdapter, 그 안에서 URI를 디코딩하고 정규화하는 postParseRequest(), Host·Context·Wrapper를 고르는 Mapper의 규칙 순서를 소스로 따라갑니다. 서블릿 여섯 개로 매핑 우선순위를, curl로 %2F·..·;jsessionid가 어떻게 처리되는지를, 붙잡아 둔 요청 객체로 RequestFacade가 왜 있는지를 직접 확인합니다.
-
Tomcat 소스 파헤치기 8: 파이프라인과 밸브 — 컨테이너마다 걸린 처리 사슬
server.xml의 <Valve> 한 줄은 소스에서 무엇이 될까요. Engine·Host·Context·Wrapper가 저마다 가진 파이프라인, 그 끝에서 다음 컨테이너를 고르는 기본 밸브 넷, 다음 밸브를 부르지 않아 요청을 끊는 법, 오류 페이지를 그리는 ErrorReportValve, 응답이 끝난 뒤에야 쓰이는 접근 로그와 RemoteIpValve의 함정까지, 직접 쓴 밸브를 세 컨테이너에 걸어 소스와 함께 확인합니다.
-
Tomcat 소스 파헤치기 9: 필터 체인과 서블릿 호출 — service()가 불리기까지
밸브 사슬의 맨 끝에서 Tomcat이 서블릿 인스턴스를 꺼내고, 이 요청에 맞는 필터를 골라 배열로 묶고, 그 배열을 한 칸씩 넘기며 마지막에 service()를 부르는 과정을 소스로 따라갑니다. 서블릿 init()이 불리는 시점, 필터 순서가 정해지는 규칙, forward와 include의 차이, 예외가 오류 페이지로 바뀌는 길도 예제로 확인합니다.
-
Tomcat 소스 파헤치기 10: 웹앱 배포와 Context 설정 — WAR 하나가 서비스되기까지
webapps 폴더에 WAR 파일 하나를 넣으면 Tomcat 안에서 무슨 일이 일어나는지 소스로 따라갑니다. HostConfig가 10초마다 폴더를 살펴 Context를 만들고, ContextConfig가 web.xml·conf/web.xml·어노테이션을 합치고 ServletContainerInitializer를 찾으며, StandardContext.startInternal()이 SCI·리스너·필터·서블릿을 정해진 순서로 깨웁니다. Spring의 SpringServletContainerInitializer가 불리는 자리도 Tomcat 쪽에서 확인합니다.
-
Tomcat 소스 파헤치기 11: 세션 — JSESSIONID는 어떻게 만들고 지우나
request.getSession() 한 줄 뒤에서 Tomcat이 세션 ID를 어디서 읽고, 어떻게 만들고, 언제 지우는지 소스로 따라갑니다. 쿠키와 ;jsessionid, SecureRandom으로 만드는 32글자 ID, 요청 때와 뒷정리 스레드에서 일어나는 두 가지 만료, 재시작 때 세션을 파일에 저장하는 StandardManager까지 예제 셋으로 직접 확인합니다.
-
Tomcat 소스 파헤치기 12: 비동기 서블릿과 논블로킹 I/O — 스레드를 놓아주는 법
startAsync()를 부른 서블릿은 응답을 끝내지 않고 돌아가고, 요청 처리 스레드는 풀로 돌아갑니다. 그 사이 요청을 붙들고 있는 AsyncStateMachine의 상태 전이, complete()와 dispatch()가 컨테이너 스레드로 돌아오는 길, 1초마다 도는 타임아웃 검사, ReadListener가 Poller와 함께 동작하는 방식을 Tomcat 소스와 스레드 이름을 찍는 예제로 따라갑니다.
-
Tomcat 소스 파헤치기 13: Jasper — JSP가 서블릿이 되기까지
hello.jsp를 처음 요청하면 Tomcat 안에서 무슨 일이 일어날까요? JspServlet이 요청을 받아 JSP 파일을 노드 트리로 읽고, 자바 소스로 옮겨 쓰고, ecj로 컴파일해 work 폴더에 .class를 남긴 뒤 그 클래스를 서블릿처럼 부르는 과정을 Jasper 소스로 따라갑니다. 생성된 .java 파일을 직접 열어 보고, JSP를 고쳤을 때 언제 다시 컴파일되는지(development, modificationTestInterval, checkInterval)도 예제로 확인합니다.
-
Tomcat 소스 파헤치기 14: 인증과 Realm — security-constraint는 어떻게 지켜지나
web.xml 의 security-constraint 와 login-config, server.xml 의 Realm, tomcat-users.xml 이 소스에서 어떻게 이어지는지 따라갑니다. 웹앱이 시작될 때 Authenticator 밸브가 끼워지는 자리, AuthenticatorBase.invoke() 의 검사 순서, URL 에 걸린 제약을 고르는 네 번의 검색, BASIC 과 FORM 의 차이, Realm 과 CredentialHandler 의 역할을 실제 소스와 401·403·303 응답으로 확인합니다.
-
Tomcat 소스 파헤치기 15: HTTP/2와 WebSocket — 업그레이드로 갈아타는 프로토콜
HTTP/1.1로 시작한 연결이 어떻게 HTTP/2나 WebSocket으로 바뀌는지 Tomcat 소스로 따라갑니다. UpgradeProtocol과 UpgradeToken, Http11Processor의 업그레이드 처리, 연결 하나에 스트림 여럿을 싣는 Http2UpgradeHandler, 필터 하나로 들어오는 WebSocket의 WsFilter와 WsHttpUpgradeHandler를 h2c 요청과 JDK WebSocket 클라이언트로 직접 확인합니다.
-
Tomcat 소스 파헤치기 16: 내장 Tomcat, JMX, 로깅 — 그리고 연재를 마치며
server.xml 없이 Tomcat을 코드로 띄우는 startup/Tomcat 클래스가 무엇을 대신 만들어 주고 무엇을 만들어 주지 않는지, 부품마다 MBean이 올라가는 JMX 등록 과정, 웹앱마다 로그 설정을 따로 두는 JULI의 ClassLoaderLogManager를 소스와 예제로 확인하고, 16편 연재를 정리합니다.
-
Spring 소스 파헤치기 5: @Autowired는 넣을 빈을 어떻게 고르나 — 의존성 주입 해석
@Autowired가 붙은 필드와 생성자에 Spring이 어떤 빈을 넣는지, 그 빈을 어떻게 찾고 여럿이면 어떻게 고르는지를 실제 소스에서 따라갑니다. 후보 찾기, @Primary·@Qualifier·이름 일치의 우선순위, List와 Map 주입, 생성자 선택 규칙, 그리고 없을 때의 처리를 예제를 돌려 확인합니다.
-
Spring 소스 파헤치기 6: 생명주기 콜백과 PostProcessor — 컨테이너에 끼어드는 자리
BeanFactoryPostProcessor, BeanPostProcessor와 그 하위 인터페이스들, Aware 계열, @PostConstruct·InitializingBean·init-method, @PreDestroy·DisposableBean·destroy-method, SmartInitializingSingleton, SmartLifecycle이 각각 언제, 어떤 순서로 불리는지 실제 소스와 실행 결과로 확인합니다. 끼어드는 자리가 왜 이렇게 여러 종류인지도 함께 봅니다.
-
Spring 소스 파헤치기 7: refresh() 12단계 — 컨테이너가 켜지는 순서
new AnnotationConfigApplicationContext(...) 한 줄 뒤에서 도는 refresh()를 열두 단계 하나씩 엽니다. ApplicationContext가 BeanFactory에 무엇을 더하는지, 왜 GenericApplicationContext와 AbstractRefreshableApplicationContext 두 갈래인지, 켜다가 실패하면 그리고 close()하면 무슨 일이 일어나는지를 실제 소스와 예제로 확인합니다.
-
Spring 소스 파헤치기 8: @Configuration과 @ComponentScan은 어떻게 설계도가 되나
설정 클래스(@Configuration)와 @Bean 메서드, @ComponentScan으로 찾은 @Component 클래스가 빈의 설계도(BeanDefinition)로 바뀌는 과정을 Spring을 처음 보는 사람 기준으로 따라갑니다. ConfigurationClassPostProcessor가 설계도를 읽어 설계도를 더하는 방법, 클래스를 로딩하지 않고 훑는 컴포넌트 스캔, 그리고 @Configuration 클래스가 CGLIB로 감싸져 @Bean 메서드를 두 번 불러도 같은 객체가 나오는 이유를 실제 소스와 예제로 확인합니다.
-
Spring 소스 파헤치기 9: @Conditional, @Profile, @Import — 조건에 따라 빈을 고르는 법
조건이 맞을 때만 빈을 등록하는 @Conditional과 @Profile, 다른 설정을 가져오는 @Import가 컨테이너 안에서 언제, 어떻게 평가되는지를 Spring을 처음 보는 사람 기준으로 따라갑니다. 조건이 세 번 평가되는 이유, @Import의 세 갈래, DeferredImportSelector가 "미루는" 이유, 순환 import를 잡는 ImportStack, 그리고 @EnableXxx가 전부 @Import의 응용인 까닭을 실제 소스와 예제로 확인합니다.
-
Spring 소스 파헤치기 10: AOP 프록시는 어떻게 만들어지나 — JDK 방식과 CGLIB 방식
ProxyFactory.getProxy() 한 줄이 JDK 동적 프록시와 CGLIB 프록시 가운데 무엇을 어떤 기준으로 고르고, 각각 어떻게 만들며, 프록시로 들어온 호출이 인터셉터 체인을 지나 대상 객체까지 어떻게 가는지 실제 소스에서 확인합니다. 자기 자신의 메서드를 안에서 부르면 어드바이스가 걸리지 않는 이유와 final 메서드의 함정도 예제로 봅니다.
-
Spring 소스 파헤치기 11: @Aspect는 어떻게 발견되고 어드바이스 체인은 어떻게 돌아가나
@EnableAspectJAutoProxy 한 줄이 컨테이너에 무엇을 등록하는지, @Aspect 클래스가 어떻게 어드바이저로 바뀌는지, 컨테이너는 어떤 빈을 프록시로 감쌀지 어떻게 정하는지, 그리고 프록시의 메서드를 부르면 @Around·@Before·@After·@AfterReturning·@AfterThrowing이 어떤 순서로 실행되는지를 실제 소스와 예제 출력으로 확인합니다.
-
Spring 소스 파헤치기 12: 이벤트, Environment, Resource — 컨테이너 주변의 세 추상화
ApplicationContext가 BeanFactory 위에 더하는 세 가지, 이벤트(보내고 받기)·Environment(설정값과 프로파일)·Resource(파일과 클래스패스 읽기)가 소스에서 어떻게 생겼고 refresh()의 어느 단계에 붙어 있는지, Spring을 처음 보는 사람 기준으로 따라갑니다. publishEvent 한 번의 여정, 시스템 프로퍼티가 설정 파일을 이기는 이유, classpath: 접두사가 클래스로 바뀌는 자리를 실제 소스와 예제로 확인합니다.
-
Spring 소스 파헤치기 13: ResolvableType과 MergedAnnotations — 타입과 어노테이션을 읽는 법
Spring이 List<String>의 String을 어떻게 알아내는지(ResolvableType), 어노테이션 위에 붙은 어노테이션과 @AliasFor를 어떻게 하나로 합쳐 읽는지(MergedAnnotations), 클래스를 로딩하지 않고 .class 파일에서 어떻게 읽는지(MetadataReader)를 Spring을 처음 보는 사람 기준으로 실제 소스와 예제로 확인합니다.
-
Spring 소스 파헤치기 14: 타입 변환과 BeanWrapper — 문자열이 객체의 속성이 되기까지
설정 파일의 문자열 "8080"이 빈의 int 필드에 들어가기까지 누가 무엇을 하는지 소스로 따라갑니다. PropertyEditor와 ConversionService가 왜 둘 다 있고 어느 쪽을 먼저 쓰는지, BeanWrapper가 "address.city" 같은 경로를 어떻게 따라 내려가는지, 컨테이너가 이 둘을 어디서 쓰는지를 Spring을 처음 보는 사람 기준으로 정리합니다.
-
Spring 소스 파헤치기 15: SpEL — 표현식은 어떻게 파싱되고 계산되나
"#{T(java.lang.Math).max(a, 3) + 1}" 같은 문자열이 토큰이 되고, 트리가 되고, 값이 되기까지를 소스로 따라갑니다. 연산자 우선순위가 파서 메서드의 호출 순서로 표현되는 방식, StandardEvaluationContext 와 SimpleEvaluationContext 가 둘인 이유, 식을 바이트코드로 바꾸는 컴파일 모드, 그리고 @Value("#{…}") 가 컨테이너 안에서 풀리는 길을 Spring을 처음 보는 사람 기준으로 정리합니다.
-
Spring 소스 파헤치기 16: 유틸리티와 AOT — 모두가 쓰는 도구 상자와 미리 계산해 두기
Spring의 모든 모듈이 가져다 쓰는 spring-core의 유틸리티(Assert, ClassUtils, ReflectionUtils, 맵 세 가지, OrderComparator)가 왜 따로 있고 어디서 쓰이며 어디가 함정인지, 그리고 Spring 6부터 들어온 AOT가 컨테이너를 빌드 시점에 "미리 계산"해 Java 소스와 RuntimeHints로 바꾸는 흐름을 실제 소스와 실행 예제로 확인합니다.
-
Spring 소스 파헤치기 17: @Transactional — 트랜잭션은 누가 열고 누가 닫나
메서드에 @Transactional 한 줄을 붙이면 누가 트랜잭션을 열고, 예외가 나면 누가 롤백하고, 정상이면 누가 커밋하는지 실제 소스로 따라갑니다. PlatformTransactionManager·TransactionDefinition·TransactionStatus 세 인터페이스, @EnableTransactionManagement가 등록하는 빈, TransactionInterceptor가 호출을 가로채는 순서, 전파 속성이 갈리는 자리, 커넥션이 ThreadLocal에 묶이는 이유, 그리고 체크 예외는 롤백되지 않는다·안쪽이 실패하면 바깥이 UnexpectedRollbackException을 받는다 같은 함정을 예제로 확인합니다.
-
Spring 소스 파헤치기 18: JdbcTemplate — 템플릿-콜백 패턴과 예외 변환
날 JDBC 코드가 매번 되풀이하는 일(커넥션 얻기, Statement 만들기, 예외 처리, 닫기)을 JdbcTemplate이 어떻게 한 곳에 모아 두고 내 코드에는 콜백만 남기는지 소스로 따라갑니다. 커넥션을 @Transactional 안에서 어떻게 같이 쓰는지, SQLException이 DuplicateKeyException 같은 Spring 예외로 바뀌는 길과 7.x의 기본 변환기가 무엇인지를 Spring을 처음 보는 사람 기준으로 정리합니다.
-
Spring 소스 파헤치기 19: ORM과 R2DBC 연동 — JPA 통합과 리액티브 트랜잭션
JPA의 EntityManagerFactory가 어떻게 빈이 되고, @PersistenceContext로 받은 EntityManager가 왜 여러 스레드가 함께 써도 안전한지, JpaTransactionManager 아래서 JdbcTemplate이 어떻게 같은 커넥션을 쓰는지, @Repository의 예외가 어디서 바뀌는지를 소스로 확인합니다. 이어서 스레드가 없는 리액티브 세계에서 R2DBC 트랜잭션이 Reactor Context에 실리는 방식을 봅니다.
-
Spring 소스 파헤치기 20: spring-web 기반 — HttpMessageConverter, WebApplicationContext, 필터
Spring MVC와 WebFlux가 함께 딛고 서는 spring-web 모듈을 처음 보는 사람 기준으로 읽습니다. HTTP 메시지를 객체로 다루는 인터페이스들, 본문을 Java 객체로 바꾸는 HttpMessageConverter와 그것을 고르는 규칙, 서블릿 컨테이너 안에서 Spring 컨테이너가 켜지는 자리(ContextLoaderListener), 그리고 한 요청에 한 번만 도는 필터가 어떻게 만들어져 있는지를 소스와 예제로 확인합니다.
-
Spring 소스 파헤치기 21: DispatcherServlet — 요청 하나가 응답이 되기까지
HTTP 요청 하나가 Spring MVC 안에서 응답이 되기까지를 DispatcherServlet의 소스로 따라갑니다. 서블릿이 켜질 때 자기 컨텍스트와 전략 객체를 마련하는 과정, 그리고 doDispatch가 핸들러를 찾아 부르고 인터셉터와 예외 처리를 끼워 넣는 순서를 Spring을 처음 보는 사람 기준으로 확인합니다.
-
Spring 소스 파헤치기 22: 핸들러 매핑과 어댑터 — @RequestMapping은 어떻게 찾고 어떻게 부르나
@GetMapping을 붙인 메서드가 언제 어디에 등록되는지, 요청이 오면 후보 가운데 어느 메서드가 뽑히는지, 뽑힌 메서드의 인자는 누가 채우고 반환값은 누가 응답으로 바꾸는지를 RequestMappingHandlerMapping과 RequestMappingHandlerAdapter의 소스로 따라갑니다. 매핑이 모호할 때 나는 두 가지 예외와, 직접 만든 인자 리졸버가 끼어드는 자리도 예제로 확인합니다.
-
Spring 소스 파헤치기 23: 예외 처리, 인터셉터, 뷰 — 요청 처리의 앞뒤와 끝
컨트롤러 메서드의 앞뒤에 끼어드는 HandlerInterceptor가 어떻게 줄을 서고 왜 preHandle을 통과한 것만 afterCompletion을 받는지, 컨트롤러가 던진 예외가 어떤 순서로 HandlerExceptionResolver를 거쳐 @ExceptionHandler 메서드에 닿는지, 그리고 뷰 이름 하나가 ViewResolver와 View를 거쳐 JSP로 forward되기까지를 실제 소스와 예제 출력으로 확인합니다.
-
Spring 소스 파헤치기 24: WebFlux — DispatcherHandler, 함수형 엔드포인트, WebClient
Spring WebFlux가 요청 하나를 응답으로 바꾸는 길을 소스로 따라갑니다. HttpHandler 아래에 WebFilter와 WebExceptionHandler가 엮이는 방법, DispatcherServlet의 리액티브 짝인 DispatcherHandler, 어노테이션 컨트롤러와 함수형 엔드포인트가 같은 길을 지나는 모습, 그리고 WebClient의 필터 사슬까지 봅니다.
-
Spring 소스 파헤치기 25: WebSocket과 메시징 — STOMP 메시지는 어떻게 흐르나
브라우저가 보낸 STOMP 메시지 하나가 @MessageMapping 메서드를 거쳐 구독자들에게 퍼지기까지를 소스로 따라갑니다. spring-messaging의 Message·채널·핸들러, spring-websocket의 핸드셰이크, 그리고 둘을 잇는 채널 세 개와 간단한 브로커를 WebSocket을 처음 보는 사람 기준으로 확인합니다.
-
Spring 소스 파헤치기 26: spring-test — SpringExtension, TestContext, MockMvc
테스트 클래스에 어노테이션 한 줄을 붙이면 컨테이너가 뜨고 빈이 주입되고 트랜잭션이 롤백됩니다. JUnit과 Spring이 만나는 SpringExtension, 같은 설정이면 컨텍스트를 한 번만 띄우는 컨텍스트 캐시와 그 열쇠, 테스트 트랜잭션, 서버 없이 진짜 DispatcherServlet을 돌리는 MockMvc를 실제 소스에서 확인합니다.
-
Spring 소스 파헤치기 27: 캐시, 스케줄링, 검증, 리질리언스 — 컨테이너 위에 얹힌 기능들
@Cacheable, @Scheduled, @Async, @Validated, @Retryable은 따로 만든 기능처럼 보이지만 속은 같은 틀을 되풀이합니다. 스위치 어노테이션이 무엇을 등록하고, 어느 후처리기가 빈을 감싸고, 어느 인터셉터가 호출을 가로채는지를 Spring을 처음 보는 사람 기준으로 소스에서 확인하고, 연재 전체를 정리합니다.
-
Spring 소스 파헤치기 1: AI와 함께 소스를 읽는 방법과 분석 지도
Spring이 무엇이고 어떻게 동작하며 어디에 쓰는지를 가장 작은 예제와 그림, 3분 실습으로 먼저 보고 시작하는, Spring 소스 읽기 시리즈의 첫 글입니다. 이어서 84만 줄짜리 소스를 작은 단위로 나눠 AI와 함께 읽는 방법, 똑같은 소스를 받는 법, 앞으로 읽을 순서를 정리합니다.
-
Spring 소스 파헤치기 2: 전체 지도 — 모듈과 패키지는 어떻게 나뉘어 있나
Spring Framework 저장소를 처음 열어 보는 사람을 위한 지도입니다. 모듈 22개가 각각 무슨 일을 하고 서로 어떻게 기대고 있는지, spring-core·spring-beans·spring-context·spring-aop 안에는 무엇이 있는지, 컨테이너가 켜질 때의 큰 흐름까지 실제 빌드 파일과 소스로 확인하며 따라갑니다.
-
Spring 소스 파헤치기 3: BeanFactory 계층 구조와 BeanDefinition
Spring이 객체를 만들기 전에 먼저 준비하는 설계도(BeanDefinition)와, 객체를 꺼내는 창구(BeanFactory)가 왜 여러 인터페이스로 나뉘어 있는지를 실제 소스 발췌와 그림으로 따라갑니다. Spring을 처음 보는 사람을 위한 글입니다.
-
Spring 소스 파헤치기 4: Bean은 어떻게 만들어지나 — 생성 흐름과 3단계 캐시
getBean()을 한 번 부르면 Spring 안에서 무슨 일이 일어나는지, Spring을 처음 보는 사람 기준으로 따라갑니다. 캐시 조회, 인스턴스화·의존성 주입·초기화 세 단계, 그리고 A와 B가 서로를 필요로 하는 순환 참조를 맵 세 개로 푸는 방법을 실제 소스에서 확인합니다.
-
Unity Practice 1: GameObject와 Controller 사용법 알아보기
Unity를 처음 켜 본 사람이 처음 켜 보는 사람에게 쓰는 실습 기록입니다. 설치부터 에디터 화면 읽는 법, 상자에 물리를 붙이고 마우스로 배트를 휘둘러 날리기, Windows 빌드까지 화면 캡처와 전체 스크립트로 따라갑니다.