안녕하세요! 그로스로그 입니다!😊 🌱
#include가 실제로 무슨 일을 하는지, 그리고 war는 만들어졌는데 왜 안에 클래스 파일이 없었는지. 2기 2회차 세 편입니다.
성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 지난 기수의 성장일지를 다시 읽으면서, 그때 소개해드리지 못한 글을 하나씩 꺼내고 있습니다.
2기 2회차, 2024년 9월 말입니다. 이번 회차는 우연이 좀 특별해요. 세 분이 각자 다른 언어와 다른 상황에서, 같은 구간을 들여다보고 있었습니다.
01
⚙️ C언어의 선행처리기
전일수 님
C 언어의 선행처리기(Preprocessor)를 정리한 글입니다. 컴파일러가 소스 코드를 번역하기 전에 먼저 도는 프로그램이에요.
다섯 가지 기능을 예제와 함께 정리했습니다. 매크로 정의(
#define)·조건부 컴파일(#ifdef)·파일 포함(#include)·매크로 해제(#undef)·기타 지시문(#pragma, #error).그리고 마지막에 한 줄로 성격을 못 박습니다. "매크로는 단순히 코드 내에서 텍스트로 치환되며, 타입 검사는 하지 않는다."
C를 배우면 #include <stdio.h>를 첫 줄에 쓰라고 배웁니다. 그런데 그게 정확히 무슨 일을 하는지는 한참 뒤에야 알게 되죠.
이 글은 그 #으로 시작하는 줄들이 컴파일 이전 단계에 속한다는 것부터 짚고 시작합니다.
조금 더 들어가 보면
📋 전처리기는 텍스트만 다룬다
가장 중요한 성격을 글이 마지막에 적어뒀습니다.
이 한 줄이 전처리기의 모든 것을 설명합니다. 전처리기는 C 문법을 모릅니다. 그냥 글자를 찾아 바꿔치기할 뿐이에요.
글에 나온 예제로 확인해보겠습니다.
#define PI 3.14 #define SQUARE(x) ((x) * (x))
SQUARE(3)이라고 쓰면 컴파일러가 보기 전에 ((3) * (3))으로 바뀝니다. 함수를 부르는 게 아니라 글자가 갈아끼워지는 겁니다.
여기서 괄호가 왜 저렇게 많은지가 설명됩니다. 만약 괄호 없이 #define SQUARE(x) x * x라고 썼다면 어떻게 될까요.
SQUARE(1 + 2)는 1 + 2 * 1 + 2가 됩니다. 우리가 기대한 9가 아니라 5가 나와요. 곱셈이 덧셈보다 먼저 계산되니까요.
전처리기는 1 + 2를 "3이라는 값"으로 이해하지 않습니다. 그냥 글자 세 개로 봅니다. 그래서 매크로를 만들 때는 인자마다, 그리고 전체에 괄호를 둘러야 해요. 글의 예제가 ((x) * (x))처럼 이중으로 감싼 이유입니다.
타입 검사를 안 한다는 것도 같은 맥락입니다. SQUARE("hello")라고 써도 전처리기는 통과시킵니다. 오류는 그 뒤에 컴파일러가 만납니다. 그래서 매크로에서 난 문제는 에러 메시지가 엉뚱한 곳을 가리키는 경우가 많습니다. 컴파일러는 이미 치환된 코드를 보고 있으니까요.
🔀 조건부 컴파일이 푸는 문제
두 번째 기능이 조건부 컴파일입니다.
printf("Debug mode\n");
#endif
위 코드에서 DEBUG가 정의되어 있을 경우에만 printf 함수가 실행됩니다.
정확히는 "실행된다"보다 "컴파일된다"가 더 맞습니다. DEBUG가 정의돼 있지 않으면 그 printf 줄은 아예 실행 파일에 들어가지 않습니다.
이게 if 문과 다른 점입니다. 일반 if는 조건이 거짓이어도 코드는 프로그램 안에 들어 있어요. 실행 중에 건너뛸 뿐입니다. 조건부 컴파일은 아예 없던 것처럼 잘려나갑니다.
그래서 쓸모가 다릅니다. 개발 중에만 필요한 로그를 #ifdef DEBUG로 감싸두면, 배포용으로 빌드할 때 그 코드가 통째로 빠집니다. 실행 파일이 작아지고 실행 속도에도 영향이 없어요. 그리고 디버그 로그가 실수로 운영에 나가는 사고도 막습니다.
글이 짚은 대로 "플랫폼이나 환경에 따라 코드의 다른 부분을 컴파일"하는 데도 씁니다. 윈도우용 코드와 리눅스용 코드를 한 파일에 두고, 컴파일할 때 필요한 쪽만 남기는 식이죠.
📎 꺾쇠와 따옴표의 차이
#include에 대한 설명에서 작지만 실용적인 구분이 나옵니다.
#include <파일명>: 시스템 경로에 있는 표준 라이브러리 파일을 포함#include "파일명": 현재 프로젝트 경로에서 파일을 포함꺾쇠는 시스템, 따옴표는 내 프로젝트입니다. 컴파일러가 파일을 찾는 순서가 달라져요.
처음 배울 때는 <stdio.h>만 쓰니 차이를 느낄 일이 없습니다. 그런데 직접 헤더 파일을 만들기 시작하면 바로 부딪힙니다. 내가 만든 myheader.h를 꺾쇠로 쓰면 시스템 경로에서 찾다가 "그런 파일 없다"는 오류가 나거든요.
그리고 #include가 하는 일도 결국 텍스트 붙여넣기입니다. 그 파일의 내용을 통째로 그 자리에 복사해 넣어요. 헤더 파일에 실제 코드를 쓰면 안 되는 이유가 여기 있습니다. 여러 파일에서 같은 헤더를 포함하면 같은 코드가 여러 벌 생기니까요.
📝 원문에서는 이렇게 정리합니다
마지막 두 지시문이 실무에서 유용합니다.
#pragma: 컴파일러에 특정 명령을 지시할 때 사용됩니다. 컴파일러마다 지원하는 #pragma 옵션이 다를 수 있습니다.#error: 특정 조건에서 에러 메시지를 출력하고 컴파일을 중단할 때 사용합니다.#ifndef VERSION #error "VERSION is not defined" #endif
이 세 줄이 하는 일이 재밌습니다. VERSION이 정의돼 있지 않으면 컴파일을 일부러 실패시킵니다.
왜 일부러 실패시킬까요. 그냥 두면 잘못된 상태로 빌드가 끝나기 때문입니다. 버전 정보가 빠진 채 배포되고, 한참 뒤에 "이게 어느 버전이지?"라는 문제로 돌아옵니다.
나중에 애매하게 터질 문제를 지금 확실히 터뜨리는 것 — 개발에서 자주 쓰는 방식입니다. 실패는 빠를수록 싸니까요.
#pragma에 붙은 "컴파일러마다 다를 수 있다"는 단서도 정확합니다. 전처리 지시문 대부분은 C 표준에 정해져 있는데 #pragma만 예외예요. 컴파일러 제조사가 자기 기능을 넣으라고 열어둔 자리라, 여기에 의존하면 다른 컴파일러에서 안 돌 수 있습니다.
큐레이터 노트
보이지 않는 단계를 눈에 보이게 만든 글이라 뽑았습니다.
C를 배우는 사람 대부분이 #include와 #define을 씁니다. 그런데 그게 컴파일러가 아니라 그 앞단의 다른 프로그램이 처리한다는 걸 아는 사람은 적어요. 문법처럼 보이지만 문법이 아니거든요.
이 구분을 알면 실제로 달라지는 게 있습니다. 매크로에서 난 오류의 메시지가 왜 이상한지, 헤더를 잘못 포함하면 왜 그런 에러가 뜨는지가 설명됩니다. 한 단계 아래를 알면 위층의 이상한 일들이 정상으로 보입니다.
그리고 다섯 기능을 나열하고 끝내지 않고, 마지막에 "텍스트 치환이고 타입 검사는 안 한다"는 성질로 묶은 게 좋았습니다. 그 한 줄이 나머지 전부를 설명하니까요.
02
🧱 [C++] 기초 문법
강단 님
C++ 기초 문법을 정리한 글인데, 시작이 선행처리입니다. "C++프로그램을 컴파일하기 전, 소스 프로그램을 가공. 컴파일러가 실제 번역할 소스 프로그램을 만듦."
그다음 문장 작성법(세미콜론), 그리고 명칭 공간(namespace)과
using으로 이어집니다.짧지만 순서가 분명합니다. 컴파일 전 단계 → 문장 규칙 → 이름이 충돌하지 않게 하는 장치.
같은 회차에 C의 선행처리기 글과 C++의 선행처리 정리가 나란히 올라왔습니다. 서로 모르고 쓴 글일 텐데 같은 자리를 짚고 있어요.
조금 더 들어가 보면
🏷️ 왜 이름을 나눠 담아야 하나
이 글에서 선행처리 다음에 나오는 것이 명칭 공간입니다. 정의가 간결해요.
동일한 명칭이라도 다른 명칭 공간에 정의되었으면 별개임
같은 이름이라도 다른 공간에 있으면 다른 것이라는 뜻입니다.
왜 이런 게 필요할까요. 프로그램이 커지면 이름이 부족해집니다.
print, list, max, count 같은 이름은 누구나 쓰고 싶어 합니다. 내 코드에 list를 만들었는데 라이브러리에도 list가 있으면 컴파일러가 어느 쪽인지 모릅니다. 라이브러리를 여러 개 가져다 쓰면 이런 충돌이 계속 생겨요.
명칭 공간은 이름 앞에 소속을 붙여 구분합니다. std::list와 mylib::list는 이름이 같아도 다른 것이 됩니다. 성이 다르면 동명이인이 구별되는 것과 같아요.
그리고 using이 그 소속 표시를 생략하게 해줍니다.
C++을 배우면 처음에 쓰는 using namespace std;가 이겁니다. 매번 std::cout이라고 쓰기 번거로우니 "앞으로 std::는 생략하겠다"고 선언하는 거예요.
편해지는 대신 잃는 게 있습니다. 소속 표시를 없애버리면 이름 충돌을 막으려고 만든 장치가 무력해지거든요.
구체적으로 어떤 일이 생기냐면, 표준 라이브러리에는 이름이 흔한 것들이 꽤 있습니다. count, distance, swap, find 같은 것들이요. 내 코드에 count라는 함수를 만들어두고 using namespace std;를 켜두면, 컴파일러가 어느 쪽을 부를지 애매해집니다.
더 곤란한 건 당장은 잘 돌아간다는 점입니다. 지금 쓰는 표준 라이브러리 버전에 겹치는 이름이 없으면 아무 문제가 없어요. 그러다 나중에 표준이 개정되면서 같은 이름이 추가되면, 내 코드는 한 줄도 안 바뀌었는데 컴파일이 깨집니다.
그래서 실무에서는 파일 전체에 거는 대신 이렇게 씁니다.
using std::cout; // 필요한 이름만 가져오기
using std::endl;
void f() {
using namespace std; // 함수 안에서만 켜기
...
}
특히 헤더 파일에는 절대 쓰지 않습니다. 헤더는 여러 파일에 복사되어 들어가니(앞서 전일수 님 글에서 본 #include의 성질이죠), 헤더에 using namespace std;를 쓰면 그 헤더를 포함한 모든 파일에 강제로 켜집니다. 내 선택이 남의 파일까지 바꿔버리는 거예요.
전처리기와 명칭 공간이 이 지점에서 만납니다. #include는 텍스트를 그대로 붙여넣으니, 헤더에 쓴 것은 전부 남의 코드가 됩니다.
⛓️ 세미콜론이 왜 필요한가
문장 작성법 항목도 짧지만 짚고 갈 만합니다.
문장 끝에는 세미콜론(;) 기입
파이썬을 먼저 배운 사람에게는 이 세미콜론이 번거롭게 느껴집니다. 줄바꿈으로 구분하면 될 것 같은데 왜 굳이 붙일까요.
C·C++은 줄바꿈을 공백과 똑같이 취급하기 때문입니다. 컴파일러 입장에서 줄바꿈은 아무 의미가 없어요. 그래서 문장이 어디서 끝나는지를 알려줄 표시가 따로 필요합니다.
대신 얻는 게 있습니다. 한 문장을 여러 줄에 나눠 쓸 수 있습니다. 조건이 긴 if 문이나 인자가 많은 함수 호출을 보기 좋게 줄바꿈해도 문제가 없어요. 파이썬에서 같은 걸 하려면 역슬래시를 붙이거나 괄호로 감싸야 합니다.
세미콜론은 불편의 대가가 아니라 자유의 대가인 셈입니다.
📝 원문에서는 이렇게 정리합니다
선행처리 지시문을 네 줄로 압축해뒀습니다.
매크로 선언 : #define
매크로 해제 : #undef
조건부 컴파일 : #if, #ifdef, #ifndef
전일수 님이 정리한 C의 선행처리기와 거의 그대로 겹칩니다. C++이 C를 바탕으로 만들어진 언어라 전처리 단계를 그대로 물려받았거든요.
그런데 C++에서는 이 중 일부를 덜 쓰는 방향으로 갑니다. 예를 들어 상수를 만들 때 #define PI 3.14 대신 const double PI = 3.14;를 씁니다. 매크로는 타입 검사를 안 하지만 const는 하니까요. 함수형 매크로도 인라인 함수로 대체합니다.
전처리기는 여전히 있지만 쓰임이 좁아진 것이죠. 이 글이 선행처리를 맨 앞에 두면서도 짧게 지나간 게, 그런 위치를 반영한 구성처럼 보입니다.
큐레이터 노트
1,961자로 짧은 글입니다. 그런데 순서가 좋아서 골랐습니다.
C++ 기초 문법을 정리하라고 하면 보통 변수·자료형·연산자부터 시작합니다. 이 글은 선행처리 → 문장 규칙 → 명칭 공간으로 갑니다. 코드를 쓰기 전에 알아야 할 것부터 놓은 거예요.
명칭 공간을 기초 단계에 넣은 것도 눈에 띕니다. 보통 한참 뒤에 배우는 개념인데, using namespace std;를 첫날부터 쓰게 되니 사실 처음부터 만나는 것이 맞습니다. 자기가 매일 쓰면서 모르고 넘어가던 줄을 짚은 셈입니다.
이분은 2기 내내 C++을 회차마다 하나씩 정리해갑니다. 포인터, 함수, 구조체와 클래스로요. 교재 순서대로 쌓아가는 기록입니다.
03
🧯 컴파일 오류 해결
장진명 님
앞의 두 편이 배우는 글이라면, 이건 실무에서 부딪힌 글입니다.
개발 환경 관리가 안 된 레거시 프로젝트를 인수인계받았는데, 안내받은 방법이 이상했어요. "war를 추출해서
.class 파일을 뽑아 부분 반영하라." 그런데 .class 파일이 안 들어 있는 경우가 많았습니다.그 이유를 추적하면서 컴파일과 빌드가 무엇이 다른지를 실제 문제로 확인해갑니다.
교과서적인 순서가 아니라 문제에서 시작해 개념으로 내려가는 글입니다.
조금 더 들어가 보면
🔨 컴파일과 빌드는 다른 일이다
글이 두 개념을 사전 정의로 나란히 놓습니다.
빌드 — 소스 코드를 실행할 수 있는 상태로 만드는 행위
읽으면 비슷해 보입니다. 그런데 실제로는 포함 관계예요. 컴파일은 빌드의 한 단계입니다.
자바를 예로 들면 빌드는 이런 일들을 묶어서 합니다. 의존 라이브러리를 내려받고 → 소스를 컴파일해 .class를 만들고 → 설정 파일과 정적 자원을 모으고 → 정해진 폴더 구조로 배치해서 → war나 jar 한 덩어리로 압축합니다.
컴파일은 그중 두 번째 단계 하나입니다.
글이 자바의 컴파일 흐름을 세 줄로 정리했습니다.
- maven compiler가 자바 소스파일을 컴파일
- 컴퓨터가 읽을 수 있는 java 바이트 코드(.class)를 생성
여기서 바이트 코드라는 표현이 정확합니다. 자바는 기계어로 바로 가지 않고 중간 형태인 .class를 만들어요. 그걸 JVM이 실행합니다. "한 번 작성하면 어디서나 돈다"는 자바의 성질이 여기서 나옵니다.
🕵️ war는 있는데 class가 없던 이유
이 글의 핵심은 여기입니다. 추적 과정이 탐정물 같아요.
먼저 컴파일을 시도합니다. 그런데 막힙니다.
그래서 jdk 버전 변경 후 컴파일을 실행해보니 각 파일에서 오류를 엄청 뱉어 냄..😂(주로 로컬에 없는 파일을 임포트 하려고 함)
JDK 버전이 안 맞고, 임포트하는 파일이 로컬에 없습니다. 오래된 프로젝트에서 흔한 조합이에요.
그래서 판단을 내립니다.
.class파일을 target에 생성해냈다..!목표를 좁힌 겁니다. 프로젝트 전체를 살리는 게 아니라 컴파일만 되게 하는 것. 글에도 이유가 적혀 있습니다. "프로젝트 실행까지가 목표가 아니기 때문에 컴파일까지만 성공해도 작업 속도를 훨씬 단축할 수 있다."
그리고 진짜 의문이 나옵니다.
답이 두 갈래로 밝혀집니다.
첫째, 이클립스의 WAR 추출은 컴파일을 강제하지 않습니다.
.class 파일은 없다당연하게도 컴파일이 되지 않은 소스파일이기 때문이다
그래서 빌드가 제대로 되지 않은 war 파일을 추출한 것이다
빈 껍데기가 나온 겁니다. 파일은 만들어졌으니 성공한 것처럼 보이지만 알맹이가 없어요.
둘째, 그동안 잘 되던 이유는 옛날 결과물이 남아 있어서였습니다.
하지만 여러 이유로 project clean을 해버리면
.class 파일을 모두 삭제하고 다시 컴파일 하여 빌드하려고 하면 컴파일이 제대로 되지 않아 .class 파일을 추출하지 못하는 것이였다이게 이 글에서 가장 값진 발견입니다. 프로젝트는 오래전부터 컴파일이 안 되는 상태였습니다. 그런데 예전에 만들어진 .class 파일이 target 폴더에 남아 있어서, 그걸 그냥 담아 배포해온 거예요.
그래서 아무도 문제를 몰랐습니다. project clean을 하는 순간 그 잔재가 지워지면서 비로소 드러난 겁니다.
🧊 잔재가 감춰주던 문제
이런 종류의 문제가 무서운 이유가 있습니다. 아무 일도 안 하면 계속 잘 돌아갑니다.
빌드 결과물이 남아 있는 한 배포는 됩니다. 그런데 그 안에 든 .class는 누가 언제 어떤 소스로 만들었는지 알 수 없는 파일이에요. 지금 저장소에 있는 소스와 일치한다는 보장이 없습니다.
그러면 이런 일이 생깁니다. 소스를 고쳤는데 반영이 안 되거나, 저장소에 없는 기능이 서비스에서 돌거나, 되돌리려는데 어떤 버전으로 가야 할지 모르거나.
요즘 CI/CD가 매번 깨끗한 환경에서 처음부터 빌드하는 이유가 이겁니다. 잔재를 남기지 않으면 "소스에서 나온 것"만 배포되니까요. 느리고 번거롭지만, 그 대가로 지금 도는 것이 어느 소스에서 나왔는지를 항상 알 수 있습니다.
이분은 그 원칙을 문서로 배운 게 아니라 잔재가 지워진 프로젝트를 떠안으면서 알게 됐습니다.
📦 부분 반영이라는 관행
이 글의 출발점이 된 인수인계 내용도 짚어볼 만합니다.
부분 반영은 전체를 다시 배포하지 않고 바뀐 파일만 서버에 올려 덮어쓰는 방식입니다. 빠르고, 서버를 재시작하지 않아도 되는 경우가 있어 예전에는 흔히 썼습니다.
그런데 대가가 큽니다. 서버에 지금 올라가 있는 것이 어느 소스에서 나온 건지 아무도 모르게 됩니다. 여러 사람이 각자 다른 시점에 다른 파일을 덮어썼다면, 그 조합은 저장소 어디에도 존재하지 않는 상태예요. 문제가 생겨도 되돌릴 지점이 없습니다.
그리고 이 관행이 컴파일이 깨진 것을 감춰줬습니다. 전체 빌드를 했다면 진작 실패했을 텐데, 필요한 파일만 뽑아 올리니 나머지가 망가진 걸 알 수가 없었던 거죠.
편한 방식이 문제를 덮어두는 경우가 있습니다. 이 글은 그 덮개를 걷어낸 기록입니다.
큐레이터 노트
1,370자입니다. 이번 회차에서 가장 짧은데, 마지막 문단 때문에 골랐습니다.
그리고 직접 개념을 하나하나 실제 문제에 대입하다 보니 컴파일 과정과 빌드 과정이 생생하게 와닿아 앞으로 절대 잊어버리지 않을 것 같다
"계속 배우기는 했지만"과 "절대 잊어버리지 않을 것 같다" 사이에 이 글이 있습니다.
컴파일과 빌드의 정의는 어디에나 있습니다. 시험에도 나오고요. 그런데 그걸 외운 상태와, 컴파일이 안 되는 프로젝트를 앞에 두고 하나씩 대입해본 상태는 다릅니다. 후자에서는 정의가 도구가 됩니다. "빌드는 컴파일을 포함한다"는 문장이 "그럼 war 안에 class가 없다는 건 컴파일이 안 됐다는 뜻이겠네"로 이어지니까요.
배운 것이 언제 자기 것이 되는지 묻는다면, 이 글이 하나의 답입니다. 그 개념이 없으면 못 푸는 문제를 만났을 때요.
💡 세 편을 겹쳐 읽으며
세 글이 다루는 언어가 다릅니다. C, C++, 자바예요. 그런데 셋 다 소스 코드가 실행 가능한 것이 되기까지의 구간을 보고 있습니다.
평소에는 안 보이는 구간입니다. 에디터에서 실행 버튼을 누르면 결과가 나오니까요. 그 사이에 무슨 일이 벌어지는지 궁금할 이유가 없습니다.
세 사람이 그 구간을 서로 다른 이유로 들여다봤습니다.
전일수 님과 강단 님은 배우다가 봤습니다. 교재 순서에 선행처리가 앞쪽에 나오니까요. 그래서 "컴파일 전에 이런 게 있다"를 정리했습니다.
장진명 님은 막혀서 봤습니다. war에 class가 없는 이유를 찾다가 컴파일과 빌드의 경계까지 내려갔어요.
같은 구간인데 도착한 방향이 반대입니다. 두 사람은 위에서 아래로, 한 사람은 아래에서 위로.
그리고 세 글을 순서대로 이으면 하나의 흐름이 됩니다.
| 단계 | 하는 일 | 다룬 글 |
|---|---|---|
| 전처리 | #include·#define을 텍스트로 치환 |
전일수·강단 |
| 컴파일 | 소스를 바이트 코드·기계어로 번역 | 장진명 |
| 빌드 | 컴파일 결과에 자원을 모아 배포물로 묶음 | 장진명 |
이 단계들이 나뉘어 있다는 걸 알면 오류를 읽는 눈이 달라집니다. 매크로 때문에 난 오류는 엉뚱한 줄을 가리키고, 컴파일 오류는 문법 문제고, 빌드 오류는 파일이 없거나 경로가 틀린 문제입니다. 어느 단계에서 났는지만 알아도 찾을 범위가 확 줄어듭니다.
그리고 세 글 다 "편해 보이는 것 뒤에 무엇이 있는가"를 봅니다. #include 한 줄, 실행 버튼 하나, war 파일 하나. 다 간단해 보이는데 그 안에 단계가 여러 개 있습니다.
이번 회차에서 가져갈 것
1. 매크로 인자에는 괄호를 두르기.
#define SQUARE(x) x * x는 SQUARE(1 + 2)에서 틀린 답을 냅니다. 전처리기는 값이 아니라 글자를 바꿔치기하니까요. 인자마다, 그리고 전체에 괄호를 씌우는 습관이 필요합니다.
2. 실패를 앞으로 당기기.
#error로 필수 값이 없으면 컴파일을 일부러 중단시키는 것처럼요. 나중에 애매하게 터질 문제를 지금 확실히 터뜨리면 원인 찾는 시간이 사라집니다.
3. 잔재를 의심하기.
장진명 님이 만난 프로젝트는 오래전부터 컴파일이 안 됐는데 남은 .class 덕분에 굴러가고 있었습니다. 한 번 깨끗이 지우고 처음부터 만들어보면 감춰진 문제가 드러납니다. 지금 도는 것이 지금 소스에서 나온 게 맞는지 확인하는 방법이기도 하고요.
4. 목표를 좁혀서 진입하기.
레거시 프로젝트를 전부 살리려 했다면 시작도 못 했을 겁니다. "실행이 아니라 컴파일까지만"으로 범위를 줄인 덕분에 한 발 들어갈 수 있었어요. 막막한 대상 앞에서 지금 필요한 최소 목표를 정하는 게 먼저입니다.
2기 2회차는 2024년 9월 말이었습니다. 새 학기가 시작되고 2주쯤 지난 시점이라, 교재 진도와 실무가 각자의 속도로 굴러가던 때예요.
같은 구간을 배우는 사람과 부딪히는 사람이 한 회차에 나란히 기록을 남겼습니다. 2주에 한 번 쓰는 활동이라 가능한 우연이라고 생각합니다.
좋은 글 남겨주신 전일수 님, 강단 님, 장진명 님께 감사드립니다. 그리고 2기를 함께 채워주신 모든 멤버분들께도요! 😊
다음 큐레이션으로 다시 찾아오겠습니다! 🌱
- 에디터 · 성장일지 큐레이터 -
※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.
'성장일지' 카테고리의 다른 글
| [GL 2기 4회차] 데이터가 지나가는 길목 — 입출력 시스템, 리액트 fetch, tap 연산자 🌱 (0) | 2026.08.09 |
|---|---|
| [GL 2기 3회차] 나누고, 모으고, 걸러내고 — React 컴포넌트와 props, reduce 연산자, includes 검색 필터 🌱 (0) | 2026.08.09 |
| [GL 2기 1회차] 백지에서 시작하지 않는다 — 퍼셉트론과 중선형회귀, 복수전공, 21개월 회고 🌱 (0) | 2026.08.09 |
| [GL 1기 7회차] 마지막 회차에 0편을 씁니다 — 엔진 분석, 낙관적 락 실습, 타임리프 화면 만들기 🌱 (0) | 2026.08.09 |
| [GL 1기 6회차] 아직 아무것도 안 만들었는데 프로젝트는 시작됐다 — 서버 구성도, 설명 자료, 색상 팔레트 🌱 (0) | 2026.08.09 |