최근 백엔드와 인프라를 공부하면서 낯선 개념을 한 번에 여러 개 마주했다.

  • Terraform
  • Docker / Container
  • AWS관련 여러 서비스들 등
  • 서버 런타임, 프로세스, 메모리 구조

프론트엔드 위주로 개발해오다가 인프라 레이어를 처음 접하니깐 알 수 없는 수많은 서비스와 용어들에 압도되어
“어디서부터 이해해야 하지?” 라는 부담부터 먼저 왔다. 

 

하지만 과거라면:

  • 책을 여러 권 사고
  • 블로그 글의 신뢰도를 검증하고
  • 선배 개발자에게 물어보고
  • StackOverflow를 뒤지며 단편적인 정보를 이어 붙이고
  • 정리하는 데만 며칠이 걸렸다

실제로 개념을 “이해”하기까지 걸리는 시간보다,
정확한 정보를 찾는 과정이 더 오래 걸렸다.

그런데 지금은 다르다.

 

-

 

AI가 개발자의 일자리를 대체한다는 담론은 계속 나오고 있다.
실제 체감으로도 느껴진다. 뉴스뿐만 아니라, 실제 경력직으로 직장을 다니고 있는 가족, 지인, 친구와 신입으로 취업을 준비하는 후배들 얘기를 들어보고 데이터로도 개발자들을 위한 일자리가 급속도로 줄어들고 있다는 것이.

 

하지만 AI덕분에 이제 의지만 있다면 누구나 배우기 쉬운 환경이 된 것도 사실이다.

AI는 학습 비용(learning cost)을 극단적으로 줄여준다.

 

Divide and Conquer - 왜 대부분의 문제는 이걸로 해결되는가

 

Divide and Conquer는 알고리즘 설계 패러다임이다.

  1. 문제를 분할(Divide)
  2. 각각을 해결(Conquer)
  3. 결합(Combine)

대표 예시:

  • Merge Sort
  • Quick Sort
  • Binary Search
  • FFT
  • Karatsuba Multiplication
 

하지만 이 개념은 알고리즘에만 적용되지 않는다.

 

개발에서의 Divide and Conquer

 

예시 1: “서버를 띄운다”는 게 이해되지 않을 때

초기 상태:

서버를 띄운다 = ???

분해하면:

  1. 서버는 무엇인가?
    • 프로세스
  2. 프로세스는 무엇인가?
    • 메모리에 로드된 실행 중 프로그램
  3. 네트워크는 어떻게 연결되는가?
    • 포트 바인딩
  4. 외부에서 접근하려면?
    • 공인 IP / NAT
  5. 배포는?
    • CI → 빌드 → 이미지 → 배포

이렇게 나누면 더 이상 “서버”라는 거대한 개념은 존재하지 않는다.

 

예시 2: Terraform이 이해되지 않을 때

초기 상태:

Terraform은 뭐지? 왜 쓰지?

분해:

  1. 인프라 리소스는 무엇인가?
    • EC2
    • VPC
    • RDS
  2. 이걸 수동으로 만들면?
    • 콘솔 클릭
  3. 클릭을 코드로 정의하면?
    • Infrastructure as Code
  4. 선언적 구성?
    • desired state
  5. plan / apply 흐름?

결국 Terraform은
“클릭을 코드로 옮긴 선언형 상태 관리 시스템”일 뿐이다.

 

 

인생 문제도 동일하다

 

예시 1: “나는 돈을 많이 벌고 싶다”

이 문장은 문제 정의가 아니다.
목표는 있지만, 변수와 경로가 없다.

이 상태에서는 해결이 불가능하다.


1. 먼저 수익 구조를 분해한다

수익은 대개 다음 네 가지로 나뉜다.

(A) 근로소득

  • 연봉
  • 성과급
  • 스톡옵션
  • 인센티브

(B) 이직 프리미엄

  • 연봉 점프
  • 직무 전환(고부가 직군)
  • 산업 변경(마진 높은 산업)

(C) 사업·사이드 프로젝트

  • 앱 수익
  • 광고 수익
  • 구독 모델
  • B2B 계약

(D) 자본소득

  • 투자
  • 지분
  • 배당

이제 문제는 “돈”이 아니라
어떤 소득 카테고리를 확장할 것인가로 바뀐다.

 

 

2. 근로소득을 쪼개보자

연봉을 올리는 방식은 세 가지뿐이다.

  1. 같은 회사에서 연봉 인상
    • 성과 평가 등급
    • 핵심 프로젝트 기여도
    • 대체 불가능성
  2. 성과급 극대화
    • KPI 구조 분석
    • 매출/지표 직결 프로젝트 참여
    • 비용 절감 기여
  3. 이직
    • 시장가 재평가
    • 연봉 점프 15~30%
    • 직무 레벨 업

예: “직장에서 돈을 더 벌고 싶다”

분해하면:

  • 회사 평가 체계는?
  • 성과급 계산 방식은?
  • 내 직무는 매출과 얼마나 연결돼 있는가?
  • 내가 대체 가능한가?

이제 “돈”이 아니라

  • KPI 기여도
  • 시장 연봉 벤치마크
  • 기술 레벨 격차
  • 산업 평균 임금

같은 측정 가능한 변수로 바뀐다.

 

 

3. 이직을 쪼개보자

“이직하면 돈이 오를까?”는 추상적 질문이다.

분해:

  • 현재 내 연봉은 시장 평균 대비 몇 퍼센트인가?
  • 내 기술 스택은 수요 대비 얼마나 부족한가?
  • 같은 직무 상위 25%는 무엇을 할 줄 아는가?
  • 산업별 평균 연봉 차이는?

이제 해결 방법은 명확하다.

  • 기술 스택 보강
  • 포트폴리오 고도화
  • 인터뷰 준비
  • 네트워킹

이건 실행 가능한 문제다.

 

4. 사이드 수익을 쪼개보자

“앱으로 돈을 벌고 싶다”

→ 추상적

분해:

  • MAU는?
  • eCPM은?
  • 전환율은?
  • LTV는?
  • 리텐션은?

이제는 “돈”이 아니라

  • 트래픽
  • ARPU
  • 전환율
  • 제작 속도

라는 변수 개선 문제다.

즉,

“돈을 많이 벌고 싶다”는 해결 불가능하다.

하지만 다음은 해결 가능하다.

  • 성과 평가 A등급 받기
  • 연봉 20% 점프 이직 준비하기
  • ARPU 15% 올리기
  • 전환율 3% → 5% 만들기

문제가 측정 가능해지는 순간, 해결 가능해진다.

 

6. Divide and Conquer 관점

원래 문제:

돈을 많이 벌고 싶다.

분해 후 문제:

  • 내 산업 평균 연봉은?
  • 내가 상위 20% 개발자가 되려면 무엇이 부족한가?
  • 성과급 계산 구조는?
  • 내 KPI 영향력은?

이제는 전략 게임이 된다.

 

결론

돈 문제는 감정 문제처럼 느껴지지만,
실제로는 구조 문제다.

  • 수익 구조를 분해하고
  • 병목을 찾고
  • 개선 가능한 변수를 고치면 된다.

돈은 결과다.

결과는 구조의 함수다.

 

Income=f(역량,시장가치,지표기여도,레버리지)

추상적인 목표를
측정 가능한 변수로 바꾸는 순간,
문제는 해결 가능한 상태로 변한다.

 

 

 

예시 2: “내 커리어가 불안하다”

분해:

  1. 시장에서 요구하는 기술은?
  2. 현재 내 기술 스택은?
  3. 격차는?
  4. 격차를 줄이는 데 필요한 시간은?

불안은 구조화되지 않은 문제에서 나온다.

 

AI 시대의 진짜 경쟁력

 

AI가 코드를 대신 써줄 수 있다.
그러나 문제를 대신 정의해주지는 않는다.

Divide and Conquer는 단순한 알고리즘 기법이 아니다.

문제 정의 능력이다.

AI는 다음을 도와준다:

  • 개념을 더 잘게 쪼개준다
  • 이해 안 되는 부분을 반복 설명해준다
  • 다른 관점으로 재설명해준다
  • 잘못된 이해를 교정해준다

 

왜 대부분의 문제는 쪼개면 해결되는가

Cognitive Load Theory (Sweller, 1988)에 따르면
인간의 작업 기억 용량은 매우 제한적이다.

큰 문제는 처리 불가능하다.

하지만 하위 문제는 처리 가능하다.

 

결론:

해결되지 않는 문제는 대부분
아직 충분히 분해되지 않은 문제다.

 

5줄 요약

  1. AI는 일자리를 빼앗는 도구가 아니라, 학습 비용을 줄이는 도구다.
  2. 대부분의 문제는 Divide and Conquer로 해결 가능하다.
  3. 개발 문제든, 커리어 문제든, 인생 문제든 구조는 동일하다.
  4. 해결되지 않는 문제는 아직 쪼개지지 않았을 가능성이 높다.
  5. AI는 쪼개는 과정을 가속하는 튜터 역할을 한다.

 

문제는 크기 때문에 어려운 게 아니라,
쪼개지지 않았기 때문에 어렵다.

 

1. 도커(Docker)가 무엇인지 부터 사용 전에 한 번 더 알아두자.

도커 = “이 앱이 돌아가려면 필요한 환경(Node 버전, OS 라이브러리 등)을 통째로 상자(이미지)로 만들어 두는 도구”

  • 네 PC에는 Node 18이 깔려 있는데, 이 프로젝트는 Node 20이 필요할 수 있음.
  • 그걸 “우리 프로젝트는 Node 20 + 이 OS에서 돌아가요”라고 Dockerfile로 정의해 두면,

어디서든 같은 상자(Docker 이미지)만 쓰면 똑같이 돌아감 (로컬, 동료 PC, AWS 서버 모두).

  • 그래서 “배포할 때도 로컬이랑 똑같은 환경”을 쓰려고 Docker를 쓰는 거고,

이 프로젝트(사내 지라 프로잭트)에서는 앱(Next.js)을 Docker 이미지로 만들어서 ECR에 올리고, 나중에 ECS에서 그 이미지를 실행하는 구조임.

 

이미지 = “그걸 실행하면 앱이 돌아가게 만든 설계도 + 필요한 것들이 전부 들어 있는 패키지

 

Docker 이미지라고 하면

  • 앱 코드(Next.js 등)
  • 실행 환경(Node 20, OS 조각 등)
  • 설치된 라이브러리(npm 패키지 등)

이걸 한 덩어리로 포장한 것 Docker 이미지임.

  • 이 덩어리를 다운받아서 “실행”하면 → 그게 컨테이너 (실제로 돌아가는 프로세스).
  • 이미지 = 설계도/패키지

컨테이너 = 그걸로 띄운 “실행 중인 앱”

 

“앱을 상자(Docker 이미지)로 만들어서, 그 상자를 AWS 창고(ECR)에 넣어 두고, AWS가 그 창고에서 꺼내서 서버(ECS)에서 실행한다.”


ECR
ECR = Elastic Container Registry = “Docker 이미지 보관하는 AWS 전용 저장소”

- Docker 이미지는 파일 하나처럼 생겼고, 용량도 꽤 큼 (몇백 MB~1GB 등).
- 그래서 이걸 어디선가 “가져다 쓸 수 있는 주소”로 두어야 함.

그게 이미지 저장소(레지스트리). ECR은 AWS가 제공하는 그 저장소임.
“우리 회사(우리 AWS 계정)만 쓰는 Docker 이미지 창고”라고 보면 됨.

역할 요약
- CodeBuild가 docker build로 Next.js 앱을 Docker 이미지로 만든 다음,
docker push로 ECR에 올림.
- 나중에 ECS가 “이 앱 실행해라” 할 때, ECR에서 그 이미지를 pull 해서 실행함.
그래서 ECR = Docker 이미지 창고.

“어떤 앱 버전을 배포할지”는 “ECR에 어떤 이미지를 올렸는지”로 정해짐.


ECS
ECS = Elastic Container Service = “Docker 컨테이너를 AWS 위에서 실행·관리해 주는 서비스”

- Docker 이미지는 “실행 방법이 적힌 설계도” 같은 거고,
이걸 실제로 돌리는 곳이 있어야 웹사이트가 동작함.
- ECS가 그 “실제로 돌리는 곳”을 제공함.

“이 ECR 이미지로 컨테이너를 만들고, 24시간 켜 두고, 트래픽 들어오면 이 앱이 처리하게 해라”를 ECS가 해 줌.
역할 나눠 보면

실행: ECR에서 이미지를 가져와서 컨테이너로 띄움.
- 그 컨테이너 안에서 Next.js(노드 서버)가 돌아가서, 사용자가 접속하면 응답함.

유지: 컨테이너가 죽으면 다시 띄우고, 부하에 따라 태스크(컨테이너 개수)를 늘리거나 줄일 수 있음 (오토 스케일).
Fargate: ECS 안에서 “서버(EC2)는 안 쓰고, 컨테이너만 관리해 주는 옵션”.
- 우리가 서버 OS 설치·패치 할 필요 없이 “컨테이너만 신경 쓰면 됨”.
그래서 ECS = “ECR에 있는 Docker 이미지를 실제 서버처럼 돌려 주는 곳”.
사내 지라 프로젝트에서는 “Next.js 앱이 돌아가는 실행 환경”이 ECS라고 보면 됨.


****EC2 안 쓴다는 말의 뜻

 EC2가 무슨 역할인

  • EC2 = AWS가 주는 가상 서버 한 대.
OS 선택, 인스턴스 타입(CPU/메모리), 디스크, 보안 그룹 등 서버 한 대를 우리가 직접 설정·관리하는 서비스임.
  • 역할: "컨테이너가 돌아 그 컴퓨터"를 우리가 직접 만드는 방식이면,
ECS 클러스터에 EC2 인스턴스를 넣고, ECS가 그 EC2 위에 컨테이너를 띄움.
  • 이걸 "ECS on EC2" 라고 부름.
  • 우리는 EC2 서버 관리(OS, 패치, 스케일, 장애 대응)까지 해야 함.

"EC2를 왜 안 쓰냐" (Fargate를 쓰면)

  • ECS on EC2
    리가 EC2 인스턴스를 만들고, 그 위에서 ECS가 컨테이너 실행.
    서버 한 대 한 대를 우리가 관리해야 함 (OS, 용량, 비용 최적화 등).
  • ECS on Fargate
    우리는 "컨테이너 몇 개, CPU/메모리 얼마" 요청
    그 컨테이너가 돌아갈 "서버"는 AWS가 알아서 만들고 관리
    .
    우리 입장에서는 EC2를 "쓰지 않는" 것처럼 보임 (EC2 콘솔에서 인스턴스 안 만들고, SSH 안 함).

그래
"서버(EC2)를 안 쓴다" = 우리가 EC2 인스턴스를 직접 만들고 관리하지 않는다.
"컨테이너만 관리" = 우리는 컨테이너(이미지, CPU/메모리, 개수)만 신경 쓰고, 그 컨테이너가 도는 호스트(서버)는 AWS가 관리한다는 뜻임.


- 우리가 EC2 서버를 직접 만들고 관리하지 않는다는 뜻임.
- 컨테이너는 결국 어떤 컴퓨터(가상 서버) 위에서 돌아감. 그 "컴퓨터"를 우리가 EC2로 띄우고, OS 설치하고, 패치하고, 모니터링하는지 아니면 AWS가 알아서 해 주는지의 차이임.

Fargate
→ 우리는 "컨테이너 이 이미지로, CPU/메모리 이만큼으로 돌려줘"만 하고,
그 컨테이너가 도는 "컴퓨터(서버)"는 AWS가 알아서 준비·관리함.
그래서 "우리는 서버(EC2)를 안 쓴다" = "EC2 인스턴스를 우리가 직접 만들고 쓰지 않는다"라고 이해하면 됨.
예)
우리가 하는 것
"이 Docker 이미지로 컨테이너 돌려줘"
"CPU 0.25 vCPU, 메모리 512MB로 해줘"
환경 변수, 로그 보낼 곳 같은 설정만 넘김

Fargate가 하는 것
- 그 요청을 받아서컨테이너가 돌 수 있는 “실행 공간”을 자기가 가진 풀에서 하나 골라서 할당하고
- 그 공간에서 이미지 pull → 컨테이너 실행
우리에게는 “태스크가 실행 중” / “중지됨” / “실패” 정도만 보여 줌

즉, “어느 서버(EC2)에서 돌리는지”는 우리가 정하지 않고, Fargate가 알아서 배치하는 구조임.




이 프로젝트에서의 연결 관계

1. 개발
Next.js 앱 코드를 CodeCommit에 푸시함.

2. 빌드 (CodeBuild)
CodeBuild가 CodeCommit에서 코드 가져와서
docker build → Docker 이미지 생성.
그 이미지를 docker push로 ECR에 올림 (예: cl-workspace:latest).

3. 저장 (ECR)
ECR이 그 이미지를 보관.
“이 버전이 지금 배포할 앱”이라고 할 수 있는 주소(이미지 URI)가 생김.

4. 실행 (ECS)
ECS가 “이 ECR 이미지로 컨테이너를 띄워라”는 설정을 가지고 있음.
새 배포가 있으면 ECS가 ECR에서 새 이미지를 pull 해서 컨테이너를 새로 띄움.
사용자가 브라우저로 접속하면, 그 컨테이너(Next.js)가 응답함.

5. DB 등
ECS에서 돌아가는 앱은 RDS(PostgreSQL)에 DATABASE_URL로 접속하고,
파일은 S3에 두는 식으로, 다른 AWS 리소스와 연동됨.

 

 

즉 도커 = 앱 실행 환경을 통째로 포장해 두는 것. 로컬/서버 동일하게 쓰려고 씀.

 

ECR에 뭐가 저장되나

  • 저장되는 : Docker 이미지 자체 (앱 코드 + Node + 라이브러리 등이 들어 있 실제 파일/레이어들).
  • 경 정보만 따로 저장되는 게 아니라, 그 환경까지 포함한 “실행 가능한 패키지 전체”가 ECR에 들어감.
  • 그걸 가리키는 소(URI)가 하나 나옴.

예: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/cl-workspace:latest

 “이 주소로 가면 저 이미지를 pull 할 수 있다”는 뜻.

정리: ECR = 이미지(실제 데이터) 저장 + 그걸 가져다 쓸 수 있는 주소 제공.

 

 ECS가 뭘 “받고” 뭘 하냐

  • ECS가 접 “이미지 파일”을 받는 건 아님.
  • ECS 설정 이미지 주소(URI)를 적어 둠.
예: “이 서비스는 ...ecr.../cl-workspace:latest 이미지를 써라.”
  • 실제로 서버를 띄울 때:
  1. ECS가 “새 태스크(컨테이너) 하나 띄워라”고 지시하고,
  2. 그 태스크를 맡은 실행 환경(Fargate 등)이 그 주소로 ECR에 접속해서 이미지를 pull 하고,
  3. 그 이미지로 컨테이너를 만들어서 실행함.
  4. 그 실행 중인 컨테이너가 곧 우리가 말하는 “서버”(Next.js 앱)임.
그래서:
  • ECR: 이미지(실제 데이터) 저장 + “가져다 쓸 수 있는 주소” 제공.
  • ECS: 그 주소를 설정으로 가지고 있다가, 필요할 때 그 주소로 이미지를 pull 해서 컨테이너로 실행 → 그게 서버가 됨
 

 

Fargate 대략적인 동작 흐름

1. 태스크 실행 요청

ECS에 “이 태스크 정의(이미지, CPU, 메모리, env)로 태스크 하나 실행해”라고 요청함.

 

2. Fargate가 “자리” 잡기

- Fargate 쪽에 이미 돌고 있는 “실행 환경” 풀이 있음 (AWS가 관리하는 컴퓨팅 리소스).

- 그중에서 요청한 CPU/메모리에 맞는 공간 할당함.

- 우리는 그게 “EC2 몇 번”인지, “어느 물리 서버”인지 전혀 모름. 추상화된 “실행 슬롯”이라고 보면 됨.

 

3. 그 자리에서 컨테이너 실행

그 슬롯에서

- ECR에서 이미지 pull

- 그 이미지로 컨테이너 시작

- 우리가 준 환경 변수·볼륨 등 적용

그래서 제로는 “어떤 컴퓨터 위에서 프로세스가 돌아가는” 구조이지만, 그 컴퓨터를 우리가 고르거나 관리하지 않음.

 

4. 동작 중

- 트래픽 들어오면 ALB 등이 그 실행 중인 태스크(컨테이너)로 요청을 보냄.

- 로그는 CloudWatch로 보내고, 우리는 “태스크 로그”만 보면 됨.

그 태스크가 돌아가는 호스트(서버)에는 우리가 SSH 같은 걸 할 수 없음.

    => 이 말은 해석하면

  • Fargate에서는 그 호스트를 AWS가 관리해서, 우리는 그 호스트에 SSH로 들어갈 수 없음이라는 말임

대신 로그·메트릭으로만 확인하는 구조라는 말임.

- 태스크 = 우리가 띄운 컨테이너 하나 (Next.js 앱).

- 그 컨테이너는 어떤 컴퓨터(가상 서버) 위에서 돌아감. 그 컴퓨터 호스트(서버).

- Fargate에서는 그 호스트를 AWS가 알아서 만들어서 우리 컨테이너를 그 위에 올려 둠.

- SSH = 그 서버(호스트) 원격으로 접속해서 로그인하는 것.

- 로그인하면 그 서버 안에서 쉘로 명령어 치고, 파일 보고, 프로세스 확인하는 식으로 직접 조작할 수 있음.

- “서버에 SSH 한다” = “그 컴퓨터에 터미널로 들어간다”라고 보면 됨.

  • Fargate일 때: 우리 태스크(컨테이너)가 돌아가는  호스트(서버)는 AWS가 만든 거라서, 우리한테 주소(IP)를 안 줌. 

 

 

**ALB: Application Load Balancer = “들어오는 요청을 여러 대상(서버/컨테이너)에 나눠 주는 서비스

ALB 왜 씀?

- ECS 컨테이너를 여러  띄우면,

“사용자는 어떤 URL로 접속하는데, 그 요청을 어느 컨테이너로 보낼지”를 정해 줘야 함.

- ALB가 그 역할을 함.

  -사용자  ALB 주소(도메인) 로 요청

  -ALB → 뒤에 있는 ECS 태스크(컨테이너) 중 하나 전달

  -그 컨테이너가 응답 → ALB가 다시 사용자에게 전달

그래서 “트래픽을 나눠 주는 것” = Load Balancer,

그중에서 HTTP/HTTPS 웹 요청을 나눠 주는  = Application Load Balancer (ALB).

 

5. 종료 시

태스크를 중지하거나 실패하면, Fargate가 그 슬롯을 반하고,

그 위에서 돌던 컨테이너는 사라짐.

비용은 그 태스크가 쓴 vCPU·메모리·실행 시간만큼만 나감 (인스턴스 1대를 24시간 빌려 쓰는 게 아님).

 

2. Docker Compose는 뭔지, 왜 로컬에서 DB까지 다 돌아가는지

Docker Compose = “도커 컨테이너 여러 개(예: 앱 + DB + Redis)를 한 번에 정의하고 같이 띄우는 설정 파일

  • 로컬에서는 “앱 서버”는 npm run dev로 직접 돌리고,

DB(PostgreSQL)랑 Redis만 Docker Compose로 띄우는 구조가 많음.이 프로젝트 docker-compose.yml “PostgreSQL 16 이렇게 띄워라, Redis 이렇게 띄워라”라고 적혀 있음.

  • docker-compose up -d 하면:
  • PostgreSQL “postgres / postgres / cl_workspace” 같은 계정·DB명이 이미 설정된 상태 컨테이너가 떠 있고,
  • Redis도 같이 떠 있음.
  • 그래서 네가 따로 “DB 계정 만들기”를 안 해도,

이미 docker-compose.yml 안에 “이 계정/비번/DB이름으로 만들어라”라고 적혀 있어서그대로 띄우면 그 계정으로 DB가 준비되는 거임.연동이 어디에 돼 있나?

  • 앱(Next.js) 쪽: .env DATABASE_URL="postgresql://postgres:postgres@localhost:5432/cl_workspace?schema=public" 처럼 “localhost 5432번 포트의 cl_workspace DB에 postgres/postgres로 접속해라”라고 적혀 있음.
  • DB 쪽: Docker Compose 같은 postgres/postgres/cl_workspace, 5432 포트로 PostgreSQL을 띄움.
  • 그래서 계정을 “너가 직접 입력”한 게 아니라,

저장소에 들어 있는 기본값(docker-compose.yml + .env.example)이 서로 맞춰져 있어서cp .env.example .env 하고 docker-compose up -d 면 “연동”이 자동으로 되는 거임.

 

Docker Compose = DB·Redis 같은 걸 “설정 포함해서 한 번에 띄우는 설정”.

계정/연동은 “코드·설정 파일에 이미 다 적혀 있어서” 별도로 받거나 넣을 게 없음.

 

** Redis

Redis = 메모리 기반 Key-Value 저장소 (In-Memory Data Store)

  • PostgreSQL은 디스크 기반 영구 저장 DB
  • Redis는 RAM 기반 초고속 저장소

 

구조적으로 보면

브라우저

→ HTTP 요청
Next.js 서버(tRPC / API Route)
→ (여기서 Redis 사용)
→ RDS(PostgreSQL) 등

Redis는 서버 내부 로직에서 사용되는 저장소다.

 

배포 환경에서는?

ECS 컨테이너 (Next.js 서버)

Redis (ElastiCache)

RDS

Redis는 ECS와 같은 VPC 내부에 존재한다.

 

 

속도 차이:

  • RDS(PostgreSQL): ms 단위 (디스크 I/O)
  • Redis: sub-ms 수준 (메모리)

Redis = “빠르게 꺼내 써야 하는 데이터를 임시로 저장하는 초고속 캐시/세션 저장소”

 

PostgreSQL vs Redis 역할 분리

구분 PostgreSQL Redis
저장 위치 디스크 메모리
속도 보통 매우 빠름
데이터 성격 영구 임시/캐시
트랜잭션 강함 약함
사용 예 유저, 이슈 세션, 캐시, 카운터

 

RDS는 영구 저장용이다.

예:

  • 유저
  • 이슈
  • 프로젝트
  • 위키

이건 반드시 영구 보존되어야 하므로 PostgreSQL에 저장.

하지만 이런 건?

  • 로그인 세션
  • 임시 토큰
  • API rate limit 카운트
  • 캐시 데이터
  • 최근 조회 데이터
  • Pub/Sub 알림
  • 작업 큐 상태

이건 굳이 디스크에 영구 저장할 필요 없다.

→ 여기서 Redis 사용.

 

항목설명

저장 위치 RAM
구조 Key-Value
속도 매우 빠름
영구성 기본적으로 휘발성
TTL 지원 자동 만료 가능

 

이 프로젝트에서 Redis는 어디에 쓰이는지?

docker-compose에 Redis가 있는 걸 보면 보통 이런 용도다:

1. 세션 저장소 (NextAuth)

로그인 세션을 Redis에 저장하면:

  • DB 부하 감소
  • 빠른 인증 체크
  • TTL 자동 만료

2. 캐시

예:

  • 이슈 리스트 조회 결과 캐싱
  • 통계 결과 캐싱
  • 자주 조회되는 데이터

흐름:

 
요청 → Redis에 캐시 있나 확인 → 있으면 바로 반환 없으면 DB 조회 → Redis에 저장 → 반환

→ DB 부하 감소 + 속도 향상


3. Rate Limit

예:

 
IP별 1분당 100회 제한

Redis에 카운터 저장:

 
INCR rate:ip:123 EXPIRE rate:ip:123 60

4. Pub/Sub (실시간 기능)

예:

  • 실시간 알림
  • 댓글 알림
  • WebSocket 브로드캐스트

5. 작업 큐 (백그라운드 처리)

예:

  • 이메일 발송
  • AI 처리
  • 로그 집계

Redis는 queue 시스템으로도 많이 쓰임 (BullMQ 등)

 

왜 Docker Compose에 Redis도 같이 띄우는지

로컬에서:

 
docker-compose up

하면

  • PostgreSQL
  • Redis

둘 다 실행됨.

왜?

→ 배포 환경에서도 Redis 쓸 거라서
→ 로컬에서도 동일하게 테스트하려고


배포 환경에서는?

로컬:

  • Docker Redis

AWS:

  • 보통 ElastiCache (Redis managed 서비스)

구조:

 
ECS 앱
Redis (ElastiCache)
RDS

 

아키텍처 전체 그림 정리

로컬

브라우저
→ Next.js
→ Prisma → PostgreSQL (Docker)
→ Redis (Docker)


배포

브라우저
→ ALB
→ ECS (Next.js 컨테이너)
→ Prisma → RDS
→ Redis → ElastiCache

 

 

3. Prisma가 뭔지, DB랑 어떤 관계인지

Prisma = “Node/TypeScript 앱에서 DB를 쓰기 쉽게 해주는 도구(ORM)

  • DB에 “사용자 넣어라, 이슈 조회해라” 같은 걸 SQL 직접 쓰지 말고,

TypeScript 코드(예: ctx.db.user.create(...))로 쓰게 해줌.

  • 떤 DB를 쓸지 연결 문자열 하나로만 정함.

이 프로젝트에서는 prisma/schema.prisma url = env("DATABASE_URL") 라고만 되어 있어서,DATABASE_URL에 뭐가 들어있느냐에 따라 “어느 DB”에 붙을지가 결정됨.Prisma 쓰려면 꼭 AWS RDS여야 하나?

  • 아님.

Prisma는 “PostgreSQL/MySQL 등에 연결만 할 수 있으면” 어디든 붙을 수 있음.

  • 로컬: Docker Compose로 띄운 PostgreSQL (localhost 5432)  DATABASE_URL이 그걸 가리킴.
  • 배포: AWS RDS(PostgreSQL)를 쓰면 → DATABASE_URL RDS 주소/계정으로 바꿔 주면 됨.

 RDS는 “배포 환경에서 쓰는 DB 서버”이고, Prisma는 “그 DB에 접속하는 방식(ORM)”임.한 줄: Prisma = 앱에서 DB 접근할 때 쓰는 도구.DB는 로컬이면 Docker PostgreSQL, 배포면 RDS 같은 “실제 DB 서버”이고, Prisma는 그 서버에 연결만 해주는 역할.

 

4. 이 프로젝트 아키텍처 — 프론트 / 백엔드 / 인프라가 어떻게 엮여 있는지

큰 그림만:

  • 프론트: 브라우저에서 보는 화면. Next.js로 만든 React 페이지들.

데이터는 tRPC로 “백엔드 API”한테 요청함.

  • 백엔드: 같은 Next.js 안에 있는 tRPC 라우터(src/server/routers/).

기서 Prisma 써서 DB를 읽고/쓰고, 그 결과를 프론트에 돌려줌.(로그인/세션은 NextAuth, 파일은 S3 Presigned URL 등.)

  • DB:
  • 로컬: Docker Compose로 띄운 PostgreSQL (같은 PC의 5432 포트).
  • 배포: AWS RDS(PostgreSQL). ====> 이건 백엔드 의사 결정에의해 Aurora PostgreSQL Serverless v2로 변경할 수도 있음

둘 다 Prisma가 DATABASE_URL로 접속함.

  • 인프라:
  • 로컬: Docker Compose(DB, Redis), .env(DATABASE_URL 등).
  • 배포: Terraform이 만든 AWS 리소스들 — CodeCommit(코드 저장), CodeBuild(빌드), ECR(도커 이미지 저장), ECS(앱 실행 예정), RDS(DB), S3(파일), SSM(비밀값) 등.

연계 흐름 (로컬 기준):

프론트 (브라우저) → tRPC 호출

엔드 (Next.js 서버, src/server/routers/) → Prisma로 DB 접근

Prisma  DATABASE_URL이 가리키는 DB에 접속 (로컬이면 Docker PostgreSQL)

DB → Docker Compose가 “postgres/postgres/cl_workspace”로 이미 띄워 둔 PostgreSQL

계 흐름 (배포 기준):

 

  1. 프론트/백 → ECS에서 도커 이미지로 실행 (같은 Next.js 앱)
  1. 그 앱의 Prisma  DATABASE_URL(SSM에서 주입)이 가리키는 RDS에 접속
  1. RDS → Terraform으로 만든 PostgreSQL 서버

도커가 왜 필요하나?

  • 로컬: DB/Redis를 “설정까지 포함해서” 같은 방식으로 띄우려고 Docker Compose 사용.
  • 배포: 앱을 “Node 버전·의존성까지 똑같이” 서버에서 돌리려고 Docker 이미지로 만들고, 그 이미지를 ECR에 넣어서 ECS에서 실행.

그래서 “도커”가 로컬(DB 띄우기)과 배포(앱 실행 환경 통일) 둘 다에 쓰이는 거임.

 

5. “계정도 안 받았고 연동도 어디에 했는지 모르겠는데” 되는 이유

  • 계정
  • DB 계정(postgres/postgres)은 우리가 만든 게 아니라,

docker-compose.yml에 이미 적혀 있는 기본값이고,그걸 그대로 쓰는 거라 “계정을 받은” 게 아님.

  • .env DATABASE_URL .env.example을 복사한 것이라,

그 안에 들어 있는 postgres/postgres/localhost가 docker-compose와 같은 값이라서 맞는 거임.

  • 연동을 “어디에” 했냐
  •  군데: .env DATABASE_URL.
  • 앱(Prisma)은 “DATABASE_URL이 가리키는 곳에 접속해라”만 하고,

그게 로컬이면 Docker Compose가 띄운 5432 포트의 PostgreSQL임.

  • 그래서 “연동 설정”은 .env 한 줄이고,

“그 한 줄이 왜 맞냐”는 docker-compose.yml과 .env.example을 우리가 서로 맞춰 놓았기 때문임.정리하면:

  • 도커 = 앱/DB 실행 환경을 통째로 포장해서, 로컬·서버 같게 쓰려고.
  • Docker Compose = 로컬에서 DB(PostgreSQL)·Redis를 “설정 포함”해서 한 번에 띄우는 설정.

계정·DB명은 여기 이미 있어서, 따로 받을 필요 없음.

  • Prisma = 앱이 DB 접근할 때 쓰는 도구.

DB는 로컬=Docker PostgreSQL, 배포=RDS. RDS는 “배포용 DB 서버”일 뿐, Prisma는 “그 DB에 붙는 방법”임.

  • 연동 = .env DATABASE_URL 한 줄.

docker-compose와 .env.example이 같은 값으로 맞춰져 있어서, 복사만 하면 “연동된 것처럼” 돌아가는 거임.

들어가며

Next.js + Prisma + PostgreSQL로 만든 사내용 프로젝트(칸반/위키 등)를 AWS에 올리면서, “어디에 어떻게 띄울까?”를 정리해 봤다.처음엔 App Runner를 쓰려다가 한국 리전(ap-northeast-2) 미지원이라 다른 선택지를 비교했고, 그 과정을 공부용으로 정리한 글이다.


1. 이 프로젝트에서 고민한 것

  • 엇을 올리는가

Next.js 풀스택 앱(Docker 이미지), RDS(PostgreSQL), S3 업로드.→ “한 개의 웹앱 + DB + 스토리지” 구조.

  • 어디에 올릴 것인가 (컴퓨팅)

코드는 Docker로 빌드하고 ECR에 올리는 전제.그 이미지를 어떤 AWS 서비스에서 실행할지를 정해야 함.

  • 제약
  • 서비스 지역: 한국 리전(ap-northeast-2) 사용 필요.
  • App Runner는 ap-northeast-2를 지원하지 않음 → App Runner 제외.

그래 App Runner 대안으로 ECS, Lambda(+ Lambda Web Adapter), EKS를 비교했다.


2. 고민한 옵션과 이유

구분 App Runner ECS(Fargate) Lambda + WebAdapter EKS
뭔가 리형 컨테이너 서비스 컨테이너 오케스트레이션(관리형 Fargate) 서버리스 함수 + HTTP 서버 어댑터 쿠버네티스
한국 리전
Docker 그대로 변환/제약 있음
RDS 연결 편함 편함(풀 유지) RDS Proxy 권장 편함
운영 부담 낮음 중간 낮음(대신 제약 있음) 높음
적합한 경우 단한 웹앱, 지원 리전이면 최적 지금처럼 “한 개 앱” 상시 운영 트래픽 적고 비용 민감 다수 서비스, K8s 팀
  • App Runner

설정 간단하고 Docker 그대로 쓰기 좋지만, ap-northeast-2 미지원이라 이번에는 제외.

  • ECS (Fargate)

Docker 이미지 그대로, RDS 연결/풀 유지하기 좋고, 한국 리전 지원.“한 개 Next.js 앱을 안정적으로 상시 운영”하기에 가장 무난한 선택.

  • Lambda + Lambda Web Adapter

서버리스, 요청당 과금.대신 콜드 스타트, 실행 시간 제한, RDS 연결 수 관리(RDS Proxy 필요) 등 고려할 점이 있어서, 트래픽이 적고 비용을 최소화하고 싶을 때만 검토.

  • EKS

확장성·유연성은 크지만,  개 웹앱만 올리기엔 운영·비용 모두 과한 선택.나중에 서비스가 많이 늘어나고 K8s를 쓸 계획이 있으면 그때 고려.


3. 이 프로젝트에서의 결론(선택 기준)

  • : 한국 리전에서 Next.js + RDS를 안정적으로 돌리기.

선택: ECS (Fargate)  1순위로 두고,

트래픽이 매우 적고 비용을 더 줄이고 싶을 때만 Lambda + Lambda Web Adapter(+ RDS Proxy) 를 대안으로 검토.

  • 용어 정리:

“ECS로 배포한다” = 컨테이너 기반 웹앱 호스팅

“Lambda로 배포한다” = 버리스 HTTP 앱 호스팅

“이번에 고민한 건 AWS 컴퓨팅/호스팅 옵션 정리” 정도로 말하면 됨.

 

 

 

사내에서 사용하는 지라를 직접 만들어보자고 파일럿 프로젝트를 제안주셔서 진행해본다.

 

바이브 코딩을 하다보면 내가 알지도 못하는 기술을 커서 IDE가 다 해주지만, 추후 운영을 위해서는 기술에 대한 스터디가 필요하기 때문에 Phase별로 쪼개서 스터디를 하고자한다. 플러터 개발하는 동안 뭐이리 새로운 기술이 많이 등장했는지 헷갈리는게 너무 많다..

 

Phase는 우선 ver1로는 총 9단계 까지가있다. 

 

Phase 1: 기반 구축

  • Next.js 14 + TypeScript 프로젝트 초기화
  • Prisma + PostgreSQL 설정
  • tRPC 설정
  • shadcn/ui 컴포넌트 설정
  • Docker + docker-compose 환경 구성 (PostgreSQL, Redis

 

1. Next.js란 무엇인가 

한 줄 정의

Next.js는 React 기반의 풀스택 웹 프레임워크로,
이 프로젝트에서는 UI + API + 인증 + 서버 로직을 하나의 코드베이스로 관리하기 위해 사용한다.


기존 React(Vite, CRA)와의 핵심 차이

                             단독                                            Next.js

 

구분 React 단독 Next.js
렌더링 CSR만 CSR + SSR + SSG
라우팅 직접 설정 파일 기반 라우팅
API 별도 서버 필요 API Routes 내장
인증 외부 서버 연동 서버 컴포넌트에서 직접 처리
배포 프론트만 풀스택 배포 가능

JIRA 같은 서비스

  • 인증
  • 권한
  • API
  • UI
    가 tightly-coupled 되어 있기 때문에 Next.js가 구조적으로 유리하다.

 

이 JIRA 클론 프로젝트에서 Next.js의 역할

이 프로젝트에서 Next.js는 단순한 “프론트엔드”가 아니다.

  • 화면(UI)
    • 대시보드, 칸반보드, 이슈 상세, 위키
  • 서버 로직
    • 이슈 CRUD, 권한 체크
  • API 레이어
    • tRPC 기반 타입 안전 API
  • 인증 처리
    • NextAuth.js 서버 연동
  • 배포 단위
    • Docker/ECS로 하나의 서비스로 배포

 

 

5년 전에 내가 리액트로 개발할때와 달라진 점

예전 (React + Redux 시절)

 
React (UI) ↓ fetch Spring / Node 서버 (API) ↓ DB
  • 프론트: React
  • 백엔드: 따로 필요
  • API 서버 필수

 

지금 (Next.js)

Next.js 하나
 ├─ 화면(UI)
 ├─ 서버 코드(API)
 ├─ 인증
 └─ DB 연결​

한 프로젝트 안에 프론트 + 백이 같이 있음

 

 

Next.js 프로젝트를 생성하면 다음이 자동으로 만들어진다. 서비스의 기본 뼈대 즉 디렉토리 구조, 빌드 시스템, 런타임 환경을 만드는 작업이다.

 

 

Next.js 프로젝트 생성

“프론트 + 백엔드가 같이 들어있는 서비스 뼈대를 만든 것”

그래서 자동으로 생긴 것들:

  • React 쓸 수 있는 환경
  • 서버 코드 쓸 수 있는 환경
  • API 만들 수 있는 구조
  • 빌드/실행 설정
jira-clone/
├── src/
│   ├── app/
│   │   ├── layout.tsx        # 전체 레이아웃
│   │   ├── page.tsx          # 메인 페이지 (/)
│   │   ├── globals.css       # 전역 스타일
│   │   └── api/              # API Routes
│   ├── components/           # 공통 UI 컴포넌트
│   ├── lib/                  # 공용 유틸
│   └── types/                # 타입 정의
├── public/                   # 정적 파일
├── package.json              # 의존성/스크립트
├── tsconfig.json             # TypeScript 설정
└── next.config.js             # Next.js 설정

 

 

  • Next.js프론트엔드 중심의 풀스택 프레임워크
    •  브라우저에 보여줄 화면을 직접 만든다
    • 동시에 서버 코드도 쓸수있어서 프론트 개발자가 백까지 같이 할때 사용
  • NestJS백엔드 전용 서버 프레임워크
    • API만 제공, 화면은 따로 리액트나 뷰가 필요, 백엔드 개발자용

 

구분 Next.js NestJS
정체 React 기반 풀스택 프레임워크 Node.js 백엔드 프레임워크
주 역할 UI + 서버 로직 API 서버
UI 렌더링 가능 (React) 불가
DB 접근 가능 가능
API 제공 가능 핵심 역할
프론트 포함 여부 포함 없음


 

Next.js의 핵심장점

 

1. 프론트 백 경계가 물리적으로 가까움.  같은 타입 타입스크립트로. 프론트와 백이 같은 Git저장소에 있기에 같은 레포. 즉 협업 유지보스가 쉬움

같은 레포의 예시)

jira-clone-repo
 ├─ app/        ← 화면 (프론트)
 ├─ server/     ← 서버 로직 (백엔드)
 ├─ prisma/     ← DB 스키마
 └─ types/      ← 공통 타입

 

Next.js + TypeScript에서 말하는 “같은 타입”

같은 레포니까 가능한 구조

 
types/issue.ts
export type Issue = { id: string title: string status: "TODO" | "IN_PROGRESS" | "DONE" }

 

서버에서 사용

function getIssue(): Issue { ... }

 

프론트에서 사용

const issue: Issue = data
 
 

서버와 프론트가 같은 파일의 타입을 씀

이게 “같은 타입”의 정확한 의미다.

예전엔

예전 방식: 타입이 분리됨 (문제의 근원)

 

백엔드

 
{ "id": "1", "title": "로그인 구현", "status": "TODO" }
 

프론트 (추측)

type Issue = { id: number // ❌ 틀림 title: string status: string }

문제:

  • 문서 안 보면 모름
  • 런타임에서 터짐
  • QA에서 발견

tRPC까지 오면

API 응답 타입을 사람이 안 만든다 -> 타입을 서버 코드에서 자동 추론 함. 타입 정의 따로 필요 없ㅇ므

 

2. API 명세 관리 지옥에서 해방! tRPC쓰면 프론트 <-> 백엔드 타입 자동 공유. 이 필드 뭐였지? 할 필요가 업음

3. 서버에서 바로 DB접근 가능 -> 불필요한 API 레이어 감소

4. 빌드 한번만하면 서버 프론트 다 배포되니까 배포가 단순화 되어서 좋음

 

---...

 

 

근데 다음 경우엔 NestJS 같은 순수 백엔드가 필요해진다.

  • 모바일 앱 API
  • 외부 파트너 API
  • 대규모 트래픽
  • 복잡한 도메인 로직
  • 여러 클라이언트(Web, App, 외부)

 

 

왜 Neon을 쓰는가

 

Neon으로 료 PostgreSQL을 들고, .env 파일의 DATABASE_URL 경하여 실행함

 

선택지                                                                                    단점

로컬 PostgreSQL 설치 환경 세팅 귀찮음, 팀 공유 불편
AWS RDS 비용 + 설정 과함
Neon 무료, 즉시 사용, URL 하나로 끝

 

 

 

tRPC / Server Component / Server Action 역할·왜 생겼는지·API랑 뭐가 다른지 기준

 

브라우저
  ↓
Client Component (프론트 UI)
  ↓
Server Component / Server Action / tRPC
  ↓
DB (Prisma)
DB는 절대 브라우저가 직접 접근 안 함

“API 레이어 감소” = HTTP 엔드포인트를 직접 설계하는 단계가 줄어듦

 

 

구분 Server Component Server Action tRPC
용도 화면 렌더링 데이터 변경 API 대체
호출 방식 페이지 로딩 사용자 액션 JS 함수 호출
fetch 필요
DB 접근
캐싱/상태 제한적 제한적 강함

 

 

불필요한 API 레이어 감소”의 정확한 의미

예전 구조

 
UI ↓ fetch REST API ↓ Service ↓ DB

Next.js + tRPC 구조

 
UI ↓ 함수 호출 tRPC / Server Action ↓ DB

사라진 것

  • URL 설계
  • HTTP 메서드
  • DTO
  • Swagger
  • 중복 타입

API는 여전히 존재하지만, 사람이 관리할 게 줄어듦


그렇다고 REST API가 이제 사용되지 않는 것이 아니다.

다음 경우엔 여전히 필요하다.

  • 모바일 앱
  • 외부 공개 API
  • 다른 서비스 연동

 이때는 NestJS 같은 백엔드 필요

오랜만에 회고를 쓴다.
마지막 회고는 2023년 초에 작성한 2022년 회고 및 2023년 목표였고, 그 이후로 2023년과 2024년의 회고를 건너뛰었다.

ChatGPT를 적극적으로 사용하면서, 블로그에 기록하고 정리하는 행위 자체에 의미를 잘 느끼지 못했다. 공부는 대부분 ‘바이브 코딩’으로 해결했고, 블로그에는 따로 남기지 않았다. 그러다 보니 블로그의 존재 자체를 잊게 되었고, 자연스럽게 회고에도 소홀해졌다.
하지만 최근 회사 동료들을 보며, 공부한 내용을 기록하고 남기는 일이 자기계발과 회고 측면에서 여전히 중요하다는 생각이 들어 다시 시작하게 되었다.


연도별 주요 정리

2023년

  • 개발 커리어(직장)  
    • 일본 회사와 프리랜서 계약하여 일본 출장 및 체류 경험
    • 첫 Flutter 개발 경험
    • 팀원없이 혼자서 처음부터 끝까지 Android, iOS 전체 개발하여 배포 운영까지 해보는 경험(그동안은 프리랜서가 아니어서 팀내에서 늘 협업을 해와서 혼자 전체 사이클을 다 해보는 경험은 소소하게 만든 앱 개발 경험밖에 없었다.)
  • 사이드
    • 유튜브 애니메이션 채널 구독자 10만 달성
    • 쿠팡, 스스 일본에서 수입하여 판매

2024년

  • 개발 커리어(직장)
    • 프리랜서 완전 재택근무하며 주도적으로 기획, 마케팅 아이디어 내며 제너럴리스트롤의 개발자로 참여
  • 개발 커리어(개인 앱)
    • Flutter로 임산부앱 코코아기 업데이트
    • Flutter 기반 먹방 게임 앱 개발 - 현재 EXIT
  • 사이드
    • 하타 요가 시작
    • 결혼

 

2025년

  • 개발 커리어(직장)
    • NFC/HCE/Gemini API 기술 앱에 적응하며 개발 역량 키움
    • Antigravity, Cursor IDE 등을 사용하여 바이브코딩 역량 키움
  • 개발 커리어(개인 앱) - 앱 4개 배포 (바이브 코딩을 통해 빠른 개발 가능)
    • 임산부를 위한 다이어리 앱 - 다운수: 180
      기존 코코아기는 Native iOS로 개발했지만, Daily Mama는 Flutter로 제작했다.
      개발 과정에서 BM을 변경하여 무료화 후 작성 시 광고 노출 방식으로 전환했고,
      광고 제거 및 일기 PDF 무제한 다운로드를 원하는 경우 영구 구매 아이템을 제공했다.
    • JLPT N3 단어장 앱 - 다운수: 44
      일본어 공부를 하며 직접 쓰고 싶어서 만든 앱.
      아직 음성 추가 등 보완할 부분이 많다.
    • 고양이 키우기 게임 - 다운수: 83
      프린세스 메이커 스타일의 2D 도트 게임.
    • 먼치팝 먹방 게임 - 다운수: 9만
      캐릭터들로 음식을 선택해 먹방하는 게임
  • 사이드
    • 잼민 대상 새로운 유튜브 채널 개설 구독자 2만 달성 (10만 유튜브는 게임 홍보용으로 유지만)
    • JLPT N4 응시 완료 (공부량이 부족해 불합격 예상)
      • 한자와 카타카나라는 초반 허들이 컸지만, 시험에 직접 응시했다는 점만으로도 만족
      • OPIC - IH
        • 12/26 금 저녁에 접수하여, 12/28 일 응시. 기존에 있던 토스 7, 토익 960 전부 다 만료돼서 2025년이 끝나기 전에 급하기 응시
        • 그런데 솔직히 이런 점수들보다 평소에 연습 많이해서 실제로 영어로 커뮤니케이션 잘하고, 영어 회화를 현실에서 많이 해보고 의사소통 할 수 있는게 더 중요한 것 같다. 그리고 토스도 오픽도 AL이랑, 8까지 받으려면 훨씬 공부도 많이 해야한다. 그냥 저냥 원래 실력으로 며칠 공부해서 가면, 공부 안 한 돌발 질문들에 생각보다 말이 잘 안 나와서 최상위 등급을 받기 어렵다. 
        • 그리고 길게 오래 공부하는걸 잘 못하고 벼락치기 스타일이라 시험도 그냥 1-2일 전에 바로 접수해버렸는데, 언어는 습관이기 때문에 진짜 실력을 높이려면 10분이라도 매일 매일 자주하는게 더 중요하다. 2026년엔 최대한 그걸 실천해보고싶다.
        • 하타요가 1년 이상 지속 중
          • 이렇게 오래 지속한 운동은 처음이라 스스로 칭찬하고 싶다.

큰 흐름으로 보면

  • 2023년: 일본 회사 프리랜서로 일하며 그 중 3개월은 도쿄에 거주하며 글로벌 서비스 개발
  • 2024년: 한국에서 프리랜서로 재택근무하며 스타트업 구성원으로써 제너럴리스트 개발자로 근무
  • 2025년: 다시 통근하는 회사에 취업하여 팀에서 대리로써 협업

2022년 말, 이직했던 스타트업이 갑작스럽게 도산하면서 진로를 다시 고민하게 되었다.
당시 늘 꿈꿔왔던 해외 취업을 목표로 호주 워킹홀리데이 비자까지 준비했고, 현지에서 개발자로 인턴부터 시작할 계획도 세웠다.

하지만 전 남친(현 남편)과 장기간 떨어지는 것이 부담스러웠다. 놓치고 싶지 않은 남자였기에!
그래서 차선으로 영어를 사용하거나 해외 출장이 잦은 환경을 찾던 중, 일본 회사에서 프리랜서로 리모트 근무할 수 있는 기회를 선택하게 되었다.

 

초반 한 달은 한국에서 리모트로 협업했고, 이후 약 3개월간 일본 출장을 다녀온 뒤 다시 한국에서 재택근무를 이어갔다. 이 경험은 아직도 생생하게 기억에 남을 만큼 좋은 시간이었다. 일본에서 통근하며 일해보는 삶, 그리고 일본의 기업 문화를 아주 조금이나마 직접 엿볼 수 있었던 경험이었다.

 

특히 기억에 남는 건 아주 사소한 일상들이다. 배고플 때 돈키호테에 들러 간식을 사 먹던 일, 아침에 카페나 세븐일레븐에 들러 커피를 사 오던 일 같은 평범한 하루들이 오히려 더 또렷하다. 일본에 거주 중이던 초등학교 동창 덕분에 매주 도쿄 근방을 함께 돌아다녔던 것도 큰 추억으로 남아 있다. 퇴근 후 라멘집이나 야키니쿠집에서 혼자 저녁을 먹던 시간들도 지금 생각하면 묘하게 아련하다.

 

한편으로는, 이 경험을 통해 내가 가족 없이 장기간 해외 생활을 하기에는 맞지 않는 사람이라는 것도 분명히 알게 되었다. 해외 생활 자체는 분명 즐거웠지만, 어디를 가든 늘 남편이나 언니가 생각이 났고, 함께 오고 싶다는 마음이 컸다.

 

한국으로 돌아온 뒤에는 남편에게 함께 일본 취업이나 해외 생활을 해보지 않겠냐고 조심스럽게 이야기를 꺼내기도 했다. 하지만 남편은 판교에서 게임 회사 기획자로 근무하고 있었고, 현실적으로 해외 생활은 쉽지 않은 상황이었다. 무엇보다 우리 둘 다 일본어를 거의 모르는 상태였기 때문에, 만족하며 다니던 회사를 떠나 노베이스로 이민을 선택하는 것은 나 스스로 생각해도 좋은 선택은 아니었다.

결국 이민 이야기는 결혼하며 자연스럽게 접게 되었다. 직접 살아보고 온 만큼 미련은 없다. 대신 열심히 일해서 자주 해외여행을 다니거나, 해외 출장을 갈 수 있는 기회를 계속 엿보는 쪽이 지금의 나에게는 더 현실적인 선택인 것 같다.

 


 

느낀 점들

 

1. 돈과 여유는 동시에 가지기 어렵다

돈을 많이 벌수록, 시간과 에너지를 더 많이 요구받는다.
프리랜서는 높은 보수를 주는 만큼 사람을 극한까지 사용한다.

일본 출장 이후 몸 상태가 급격히 나빠졌고, 한국에 돌아온 뒤 염증으로 인해 백혈구 수치가 비정상적으로 상승해 일주일간 입원하며 항생제 치료를 받았다.


그 시기에는 일본 업무뿐 아니라, 신기술과 AI 학습, 추가 프리랜서 작업까지 병행하고 있었다.

쓰리잡을 하고 있어서 수면 부족 및 스트레스 강도가 너무 높았다.

한 달에 약 1,000만 원을 벌기도 했지만, 단 한 달로 끝났다.
결국 얻은 교훈은 명확했다.

“월 천만 원”이라는 숫자를 볼 때는,
그 수익이 얼마나 오래, 얼마나 지속 가능했는지를 반드시 봐야 한다.

건강과 시간을 모두 소모해 번 돈은, 결국 다음 달 병원비와 회복 비용으로 되돌아갔다.

 

앞으로는 평일에는 본업(회사) + 운동에만 집중하며, 주말이나 남는 시간에 사이드를 하며 자기계발을 이어 나가는 것을 다짐했다.

퇴근 후 쉬거나 운동하는 시간도 없다면 멀리 갈수 없다.


2. 솔로프리너의 현실

AI는 2023년 이후 상상 이상으로 빠르게 발전했다.

이제는 관리자 한 명이 AI를 활용해 팀원 1~3명의 역할을 대체할 수 있을 정도다.

그만큼 솔로프리너 관련 콘텐츠도 넘쳐났다. 누구나 혼자서 개인 서비스를 창업해 월급만큼 벌 수 있을 것처럼 보이지만,

현실은 결코 녹록지 않다.

 

현재 개인 앱 4개와 유튜브 채널 2개(10만, 2.5만)를 운영하고 있지만,

2025년 사이드 프로젝트 기준 연 매출은 약 300~400만 원 수준이다. 앱과 유튜브 수익 비중은 절반 정도로 비슷하다.

성공 사례를 보며 다시 의지를 다지기도 하지만, 개발·디자인·마케팅·운영을 모두 혼자 해보며 이 세계가 얼마나 어려운지 매번 체감하고 있다.

 

결국 답은 하나뿐인 것 같다.
계속해 보는 것.
그리고 지속하려면 반드시 재미가 있어야 한다.

세상이 원하는 것을 찾는 것도 중요하지만, 그것을 만들어 가는 과정을 즐기지 못하면 오래 버티기 어렵다. 무엇이든 오래 쌓여야 비로소 의미 있는 결과로 이어진다.

 

이 지점에서 사회 초년생때 보던 워렌 버핏의 말이 다시 떠오른다. 솔로 프리너를 할 때도 도움이 되는 말이다.
“초봉에 집착하지 말고, 내가 즐길 수 있는 일과 함께 일하는 사람을 보라.”

즐겨야 오래 할 수 있고, 시간이 쌓여야 큰 결과가 나온다.

 

결국 좋아하는 일, 소재를 선택하는 것이 가장 현실적인 전략이라는 생각이 든다.

 


3. 회사는 ‘나가라고 할 때까지’ 다니는 것도 하나의 선택이다

AI 시대일수록 회사에만 올인하는 삶은 위험하다고들 말한다.

정년이 점점 짧아지고 있고, 최근에는 「김부장」 드라마나 책을 통해 이런 불안을 더 실감하는 사람들도 많아진 것 같다.

물론 회사에 올인하지 않는다는 말이 곧 퇴사를 꿈꾼다는 뜻은 아닐 것이다.

 

그럼에도 나는 오히려 회사에 더 오래 남고 싶다는 생각이 들었다.

회사는 내가 직접 창업하지 않는 이상, 정해진 시간 동안만 경험할 수 있는 공간이기 때문이다. 젊을 때 동료들과 부딪히며 함께 일하는 경험은 생각보다 훨씬 소중했다.

 

프리랜서를 오래 하며 대부분의 결정을 혼자 내리다 보니, 여러 사람이 모여 집단지성으로 큰 프로젝트를 만들어 가는 경험이 얼마나 값진지 더 분명하게 느끼게 되었다. 이는 좋은 동료들을 만났기에 가능한 경험이기도 했다.

 

또 한 가지 분명해진 사실은, 회사 밖에서 월급만큼의 안정적인 수익을 만드는 일이 결코 쉽지 않다는 점이다.

퇴근 후와 주말의 대부분을 서비스 개발에 쏟아부었지만, 그만큼의 수익을 만들기는 생각보다 훨씬 어려웠다.


 

다가오는 2026년 목표

커리어

  • 앱 서비스(+백엔드) 풀스택 개발 지속 그러나 BM까지 생각하며
    • 회사에서 추가 프론트엔드 업무 지원하여 더 적극적으로 진행 
    • 메모장에 쌓아둔 아이디어를 하나씩 구현
    • 성공하면 좋고, 실패해도 개발자로서의 역량이 쌓인다고 생각하기
    • 결국 나는 ‘만드는 게 재미있어서’ 개발자가 되었기 때문
    • Nest.Js 등 백엔드는 반드시 구축해볼 것 -> 풀스택으로 자체 서비스 만들어 운영해보기 

유튜브

  • 수익 목적이 아닌 취미로 운영
    • 요가, 애니메이션 등 정말 좋아하는 분야 위주
    • 트렌드와 수익 압박에서 벗어나 즐기기
  • 영어·일본어 스피킹 중심 학습
    • JLPT N3, OPIC AL 등 목표로 자격증 응시 예정
    • 출퇴근 자투리 시간에 유튜브 콘텐츠로 꾸준히 학습

 

이제는 당장의 이익보다, 오래 즐길 수 있는 것을 선택하고 싶다.
수익을 내는 방법은 이미 충분히 시도해봤고, 오히려 좋아하는 일로 오래 즐길 때

성과는 따라오는 것 같다.

 

그래서 2026년은 조금 더 나답게 가보려 한다.

문제

사진 업로드시 원본사진과 썸네일 사진을 GCS에 업로드하는데, 이 때 한 게시물에서 올리는 사진 갯수가 많아짐에 따라 사용자는 아무 것도 할 수 없게 된다. 그래서 우선 아래와 같은 처리를 했다.

 

1. 등록 후 화면을 게시물 작성 화면을 벗어나 게시물 목록에서 다른 작업을 하거나 게시물을 읽어도 다른 스레드에서 사진이 업로드 되도록 하였다.
2. 사용자가 등록하는 사진이 10장이라면, GCS에는 썸네일 10장, 원본 파일 10장으로 총 20장이 올라간다. 이때 썸네일은 용량 축소를 위해 사진 크기와 품질을 떨어트려준다.
3. 사진이 10장이라면 10장이 업로드가 동기적으로 실행된다. 하지만 10장의 업로드를 병렬처리 한다면 시간을 축소할 수 있다.

 

Dart의 비동기 처리

Dart의 비동기 프로그래밍에서는 async 및 await 키워드를 사용하여 코드를 실행할 수 있다. 이렇게 하면 여러 작업이 동시에 실행되는 것처럼 보이지만, 실제로는 이벤트 루프를 사용하여 한 번에 하나의 작업만 처리된다. 이벤트 루프는 작업을 실행할 때마다 작업을 완료할 때까지 기다리지 않고 다음 작업으로 이동한다. 이로 인해 코드는 동시에 실행되는 것처럼 보이지만, 실제로는 하나의 작업이 완료되고 나면 다른 작업이 수행된다.

 

하지만 병렬 처리에서는 여러 작업이 동시에 실행되므로, 동시에 실행되는 것처럼 보이는 것이 아니라 실제로 여러 작업이 동시에 수행된다. 이는 여러 스레드를 사용하여 동시에 작업을 처리하는 것과 비슷한 개념이다.

 

그럼 Future.wait() 이란?

Dart에서 여러 비동기 작업을 동시에 실행할 수 있도록 지원한다. Future.wait()는 여러 Future 객체를 인수로 받아서, 이들이 모두 완료될 때까지 기다리는 역할을 한다.

 

Future.wait()를 사용하면, 각각의 비동기 작업이 별도의 이벤트 루프에서 동시에 실행되어, 여러 작업을 동시에 처리할 수 있는 것이다. 이로 인해 전체 작업 시간이 단축된다. 하지만 Dart의 병렬 처리는 실제 스레드를 사용하는 것과는 약간 다르다. Dart는 단일 스레드에서 실행되며 이벤트 루프를 사용하여 비동기 작업을 처리한다. 이렇게 하면 여러 작업이 동시에 실행되는 것처럼 보이지만, 실제로는 이벤트 루프를 통해 비동기 처리가 이루어진다.

 

Future.wait()를 사용하면 여러 작업을 병렬로 처리할 수 있지만, 이는 실제로 다중 스레딩이 아닌 이벤트 루프를 사용한 비동기 처리를 의미한다는 것이다.

 

여기서 잠깐 보고 넘어갈 것 - 동시성 프로그래밍과 병렬처리의 개념

병렬 처리(parallel processing)와 동시성 프로그래밍(concurrent programming)은 서로 관련되어 있지만, 정확히 같은 의미는 아니다. 두 개념을 비교해 보자.

  1. 동시성 프로그래밍(Concurrent programming): 동시성 프로그래밍은 프로그램의 구조를 설계하는 방식으로, 여러 작업이 독립적으로 실행되거나 상호 작용하는 것처럼 보이게 한다. 동시성은 작업이 서로 인터리브(interleaved)되어 실행되는 것처럼 보이게 하여 여러 작업을 동시에 처리할 수 있다. 이러한 방식으로, 작업들이 동시에 실행되는 것처럼 보이지만 실제로는 한 번에 하나씩 처리될 수도 있다.(예: 단일 코어 프로세서에서의 실행).
  2. 병렬 처리(Parallel processing): 병렬 처리는 프로그램이 여러 작업을 동시에 실행하도록 하는 방식이다. 이는 멀티코어 프로세서 또는 별도의 프로세서에서 동시에 작업을 수행함으로써 가능하다. 병렬 처리는 여러 작업이 실제로 동시에 수행되어 처리 시간을 줄일 수 있다.

결론적으로 동시성 프로그래밍여러 작업을 독립적으로 실행되거나 상호 작용하는 것처럼 보이게 하는 프로그래밍 방식이며, 병렬 처리는 여러 작업을 실제로 동시에 실행하는 방식이다. 병렬 처리는 동시성 프로그래밍의 한 형태로 볼 수 있지만, 반드시 동일한 것은 아니다. 동시성은 병렬 처리를 구현할 수 있는 프로그래밍 기술이며, 병렬 처리는 이러한 동시성 기술을 활용하여 작업을 실제로 동시에 실행하는 것이다.

 

그럼 Future.wait()으로 다시 돌아와보자. 너는 동시성 프로그래밍이니 병렬처리니?

Dart의 Future.wait()는 동시성 프로그래밍에 속한다. Future.wait()는 여러 개의 비동기 작업(Future)이 완료될 때까지 기다리고, 모든 작업이 완료되면 결과를 반환하는 기능을 제공한다. 이를 통해 여러 비동기 작업을 동시에 실행하는 것처럼 보이게 하는 것이다.

 

그러나 Future.wait() 자체는 병렬 처리를 구현하지 않는다. Dart는 기본적으로 단일 스레드 프레임워크이기 때문이다. 그러나 Dart의 비동기 프로그래밍 모델은 멀티스레드 환경에서의 병렬 처리와 유사한 성능 향상을 제공할 수 있다. 이는 Dart가 이벤트 루프와 비동기 작업을 이용하여 다양한 작업을 효율적으로 관리할 수 있기 때문이다.

 

 

결론

이미지 업로드와 같이 네트워킹 할 시 사용자 경험과 앱의 성능을 향상시키기 위해 Future.wait()을 사용해보자. 나또한 이를 사용하여 기존 코드를 리팩토링 하였고 GCS 업로드 및 API와의 통신 속도가 비교도 안되게 빨라졌다.

 

 

더 좋은 의견이 있다면 말씀 부탁드립니다.

 

 

pushAndRemoveUntil and pushReplacement are two navigation methods in Flutter's Navigator widget.

 

pushAndRemoveUntil pushes a new route onto the navigator stack and removes all the routes below it until a specified route is encountered. It takes two arguments:

  1. The context in which the Navigator is mounted.
  2. A RoutePredicate specifies the route that should stop the removal process

Navigator.pushReplacement(
  context, 
  MaterialPageRoute(builder: (context) => NewRoute()),
);

 

pushReplacement pushes a new route onto the navigator stack and removes the current route from the stack.

 

 
Navigator.pushAndRemoveUntil(
  context, 
  MaterialPageRoute(builder: (context) => NewRoute()), 
  ModalRoute.withName('/'),
);

 

In short, pushReplacement only removes the current route, whereas pushAndRemoveUntil removes all the routes below it until a specified route is encountered.

devel이라는 개발계 브랜치에서 A라는 브랜치에 작업을 하다 커밋, 푸시, devel에 머지시켰다.

이후 A 를 devel 브랜치에 merge시킨 것을 취소하고 싶으면 아래 순서대로 진행한다. 

 

 

가끔 Revoke 시킬 일이 있는데, 순서를 헷갈리지 않기 위해서 기록해둔다.

 

 

To revoke a branch merge into the "devel" branch on GitHub, you will need to use the command line to create a new branch that reverts the changes made by the merge commit. Here are the steps:

 

  1. Checkout the "devel" branch: git checkout devel
  2. Create a new branch to revert the merge: git branch revert-merge
  3. Checkout the new branch: git checkout revert-merge
  4. Revert the merge commit: git revert -m 1 <merge_commit_hash>
  5. Push the new branch to GitHub: git push -u origin revert-merge
  6. Open a pull request on GitHub to merge the "revert-merge" branch into the "devel" branch

Note: <merge_commit_hash> is the hash of the merge commit that you want to revert.

 

 

요즘 내 머릿속엔 플러터에 익숙해지고 싶다는 생각뿐이다. 새로운 회사에서 커뮤니티 앱을 만드는 프로젝트에 참여하게 됐다. 어떤 서비스인지 보안상 자세히는 말할 수는 없지만 어린 시절 해외에서 유학하며 외롭고 힘들었던 나에게도 소개해주고 싶을 정도로 유용한 서비스가 될 것 같다. 타지에서 힘들지만 강인하게 살고 있는 사람들을 위한 앱이다. 그래서 해당 프로젝트에 더 애착이 가고 잘 해내고 싶은 욕심이 생긴다.

 

플러터(다트)가 아예 처음인데, 그래도 이전에 React 프론트엔드와 iOS(SwiftUI)를 해왔던 것이 플러터를 배우는데 많이 도움이 된다. 앞선 두 가지를 개발하는 방식과 플러터는 많이 닮아있다. 선언형 프로그래밍인 것과 UI를 구성하는 방식이 비슷하다. VSC를 사용하는 것은 리액트를 개발할 때 익혀두어 많은 도움이 됐다.

 

실제로 개인 앱 서비스를 만들고 출시하다 보니 iOS만으론 한국에서 사용자를 늘리는데 한계가 있었다. 한국엔 안드로이드 사용자가 너무 많다. 그래서 안드로이드 개발에 대한 니즈를 계속 느끼고 있었는데, 이번에 회사 프로젝트에서 플러터를 배워야 하는 상황이 되면서 냉큼 플러터를 배우면서 개발하겠다고 했다.

 

지금 프로젝트에서 나는 새로운 사람들과 일을 하고 있다. 그렇기 때문에 내가 그들에게 증명해야 하는 것은 아래 두 가지다. 2가지는 결국 연결되어 있다.

1) 내가 회사에서 주는 급여보다 훨씬 더 많은 가치를 창출해내는 사람이라는 것이다. 나에게 주는 급여가 아깝지 않도록 더 큰 가치를 창출한다.
-> 뭐든 맡기면 책임감 있게 완성도 높은 결과물을 만들어 낸다. 알잘딱깔센!
2) 앞으로도 계속 함께 일하고 싶고 기분 좋게 프로젝트를 할 수 있는 신뢰 가는 사람이 되는 것이다.
-> 커뮤니케이션에 적극적이며 늘 친절하고 겸손하게 동료들을 대한다.

 

 

위를 위해서는 당분간 밤낮 가릴 것 없이 계속 플러터에 익숙해지도록 공부해야한다. 지금 어렵다고 느껴지는 건 대부분 익숙하지 않아서다. 리액트도 처음 시작할 때 그랬다. 처음엔 모든 게 너무 낯설고 벅찼지만 한 두 달 머리 터지게 하다 보니 어느 정도 코드가 눈에 보이고 어떤 걸 찾아서 개발하면 되는지 눈에 들어오기 시작했다. 이때 깨달은 것은 조급해하지 않고 기초+이론공부를 병행하며 프로젝트에서 필요한 부분을 찾아서 만들어 나가는 것이다. 처음에 조급하고 빨리 만들어야 한다는 압박감에 이해 없이 코드를 복붙+짜깁기 하다 보면 나중에 복잡한 로직을 해결 못하고 실력이 제자리걸음이게 된다. 

 

두 번째 관심사는 일본어다. 아무래도 일본회사이고 한국 사람들이라 하더라도 일본어에 유창하기 때문에 나도 잘하고 싶다는 생각이 들었다. 나는 일본 여행도 자주 가고 일본 애니메이션 등 일본 문화를 좋아한다. 하지만 일본어를 배우기 전에 항상 영어나 더 잘하도록 노력하자 이런 마음이 있었다. 유학 다녀온 게 아까우니 영어를 까먹으면 안 된다는 강박관념이 있었다. 그래서 일본어는 늘 영어보다 뒷전이었다. 친구들은 그래도 고등학교 때 제2외국어로 일본어를 선택해서 히라가나 가타카나라도 알던데 나는 제2 외국어가 중국어인(정확히 말하면 공용어가 영어이고 mother tongue ex.중국어를 따로 배우는) 나라에서 공부를 했다. 어쨌든 언어 공부를 안 한 지 꽤 되었기 때문에 일본어도 배워보려고 한다. 우선 회사 업무가 어느 정도 손에 익으면 시작할 것이다. 

 

나의 2023년 목표는 아래와 같다.
1) 플러터 개발 잘하기 (누가 무엇을 맡기든 찾아서 만들 수 있는 정도로)
2) 일본어 공부 N2 -> 출장 두 달차 의견: 그냥 영어나 더 하자^^ 

 

 

유후인 킨린호수

 

하나 해결하면 다른거 터지고 다른거 터지고! 깃에서 이전 개발자가 만들어 놓은 프로젝트를 실행하는 것만해도 시간이 오래 걸린다.

error build: Command PhaseScriptExecution failed with a nonzero exit code

 

터미널에서 프로젝트 경로로 가서 아래 코드 실행

rm -rf pubspec.lock
flutter pub cache clean
flutter clean
flutter pub get

 

ios폴더에서 아래 명령어 작성

rm -rf Podfile.lock (존재할 경우)
rm -rf Pods (존재할 경우)
rm -rf ~/Library/Developer/Xcode/DerivedData/*
pod deintegrate
pod cache clean --all

// Delete your .xcworkspace

(M1이면) arch -x86_64 pod install --repo-update --clean-install
(M1이 아니면) pod install --repo-update --clean-install

나같은 경우 (M1이면) arch -x86_64 pod install --repo-update --clean-install 가 안먹어서

(M1이면) arch -x86_64 pod install 로 실행

 

xcode를 켜서 커맨드+쉬프트+K로 빌드클린

 

그리고 빌드 실행 해보았다.

아래와 같은 오류가 발생했다.

 

Showing Recent Messages

Copied /Users/dahaekim/Downloads/client-flutter-main/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.xcframework/ios-arm64_i386_x86_64-simulator to /Users/dahaekim/Library/Developer/Xcode/DerivedData/Runner-azxgobsobqkauddpblsfzffbbkym/Build/Products/Debug-iphonesimulator/XCFrameworkIntermediates/FirebaseAnalytics/AdIdSupport

 

아래처럼 Info -> Configuration 설정에서 Debug, Release, Profile모두 Pod으로 설정하고 Build Active Architecture Only를 모두 No로 변경함

 

그리고 FirebaseAnalytics를 Pod에서 지우고 재 설치를 해도 안되는데..

 

 

* 출처

https://velog.io/@jungti1234/Flutter-iOS-xcode-FlutterFlutter.h-file-not-found-%ED%95%B4%EA%B2%B0%EB%B0%A9%EC%95%88

 

[Flutter / iOS / xcode] 'Flutter/Flutter.h' file not found 해결방안

이 에러가 나는 원인은 정말로 모르겠다.결론적으로 다른 삽질 시도하는 것보다,좀 부담스러워도 프로젝트 내의 ios 폴더를 재생성하는 방법으로 해결하는 것이 최고다.삽질은 이미 내가 충분히

velog.io

 

+ Recent posts