개발을 하다 보면 final.txt , final_진짜.txt , final_진짜최종.txt 처럼 파일을 복사해서 이전 상태를 남기고 싶을 때가 있다. 작은 작업에서는 버틸 수 있지만 파일이 많아지고 수정 횟수가 늘어나면 무엇을 언제 왜 바꿨는지 추적하기가 급격히 어려워진다. Git은 이런 문제를 해결하는 분산 버전 관리 시스템(Distributed Version Control System) 이다. 단순히 파일을 백업하는 도구가 아니라, 프로젝트의 변경 이력을 버전 단위로 기록하고 과거 상태를 확인하거나 다시 돌아갈 수 있게 해준다. 이 글에서는 Git을 처음 접할 때 꼭 이해해야 하는 흐름을 중심으로 정리한다. Git과 GitHub는 같은 것이 아니다 처음에는 Git과 GitHub를 같은 것으로 생각하기 쉽다. Git : 내 컴퓨터에서 버전을 관리하는 프로그램 GitHub : Git 저장소를 인터넷에 보관하고 협업할 수 있게 해주는 서비스 GitHub가 없어도 Git은 사용할 수 있다. 반대로 GitHub와 비슷한 서비스로 GitLab, Bitbucket 등도 존재한다. 즉 구조는 다음과 같다. 내 컴퓨터 └─ Git ├─ 버전 생성 ├─ 과거 버전 확인 ├─ 브랜치 관리 └─ 병합 인터넷 ├─ GitHub ├─ GitLab └─ Bitbucket Git을 먼저 이해하고 그다음 원격 저장소 서비스를 연결한다고 생각하면 훨씬 덜 헷갈린다. 저장소를 만드는 git init Git으로 관리할 프로젝트 폴더에서 다음 명령을 실행한다. git init 실행하면 프로젝트 내부에 .git 디렉터리가 생성된다. my-project/ ├─ .git/ ├─ src/ └─ README.md .git 에는 커밋, 브랜치, 태그, 설정 등 Git 저장소의 핵심 정보 가 들어 있다. 일반적인 프로젝트 파일처럼 임의로 수정하거나 삭제하면 저장소 이력이 손상될 수 있으므로 직접 건드리지 않는 편이 좋다. 현재 상태는 다음 명령으로 확인할 수 있다. git status Git의 핵심 구조: Working Directory → Staging Area → Repository Git을 이해할 때 가장 중요한 구조다. Working Directory │ │ git add ▼ Staging Area │ │ git commit ▼ Repository (.git) Working Directory 현재 직접 파일을 만들고 수정하는 프로젝트 공간이다. 예를 들어 app.js 를 수정하면 우선 Working Directory에 변경이 생긴다. Staging Area 다음 커밋에 포함할 변경사항을 고르는 대기 공간 이다. 수정된 모든 파일을 무조건 하나의 버전으로 만들 필요는 없다. 여러 변경 중 관련 있는 것만 선택해서 하나의 커밋으로 묶을 수 있다. git add app.js 여러 파일을 한 번에 추가하려면 다음처럼 사용할 수 있다. git add . 다만 무조건 git add . 부터 누르기보다 git status 와 변경 내용을 먼저 확인하는 습관이 좋다. Repository Staging Area에 올라온 변경사항을 커밋하면 저장소에 새로운 버전이 만들어진다. git commit -m "회원 로그인 화면 추가" 정리하면 다음과 같다. 파일 수정 = 아직 버전 아님 git add = 이번 버전에 넣을 변경 선택 git commit = 선택한 변경을 하나의 버전으로 기록 처음 커밋할 때 사용자 정보 설정하기 Git을 처음 설치한 환경에서는 커밋 작성자 정보가 없어 오류가 발생할 수 있다. 전역 설정은 다음처럼 한다. git config --global user.name "Your Name" git config --global user.email "you@example.com" 현재 설정을 확인하려면 다음 명령을 사용할 수 있다. git config --global --list 회사 계정과 개인 계정을 분리해야 한다면 저장소별 설정도 가능하다. git config user.name "Work Name" git config user.email "work@example.com" --global 을 빼면 현재 저장소에만 적용된다. 커밋은 파일이 아니라 프로젝트의 스냅샷이다 커밋은 단순히 "수정한 파일 하나를 저장한다"는 개념보다 프로젝트가 그 시점에 어떤 상태였는지를 기록한 스냅샷 에 가깝다. 예를 들어 다음과 같이 프로젝트가 변했다고 해보자. A ── B ── C A: 프로젝트 생성 B: 로그인 기능 추가 C: 로그인 오류 수정 각 커밋은 이전 커밋과 연결되어 하나의 히스토리를 만든다. 커밋 목록은 다음 명령으로 확인한다. git log 간단하게 보고 싶다면 다음 옵션이 편하다. git log --oneline 브랜치 흐름까지 확인하려면 다음 조합이 유용하다. git log --oneline --graph --decorate --all Commit ID는 버전을 식별한다 각 커밋에는 고유한 Commit ID 가 있다. 7f3a2d1 로그인 기능 추가 0a91bc7 초기 프로젝트 구성 실제 Git의 객체 ID는 해시로 만들어진다. 일반적으로 로그에서는 앞부분만 줄여서 표시한다. 커밋의 내용이나 부모 커밋, 작성자, 메시지 같은 정보가 달라지면 다른 커밋 객체가 되므로 ID도 달라진다. 이 때문에 마지막 커밋 메시지만 수정해도 커밋 ID가 바뀔 수 있다. git commit --amend -m "회원 로그인 기능 추가" --amend 는 기존 커밋 자체를 덮어쓰는 것이 아니라 수정된 내용을 가진 새 커밋을 만들고 브랜치가 새 커밋을 가리키게 하는 동작 으로 이해하면 좋다. 이미 원격 저장소에 공유한 커밋을 amend하면 다른 사람의 히스토리와 달라질 수 있으므로 주의한다. HEAD는 현재 내가 바라보고 있는 위치다 Git을 이해할 때 자주 등장하는 것이 HEAD 다. 보통 HEAD 는 현재 체크아웃한 브랜치를 가리킨다. A ── B ── C ↑ main ↑ HEAD 현재 main 브랜치에서 새 커밋 D를 만들면 main 이 D로 이동하고, HEAD는 계속 main 을 따라간다. A ── B ── C ── D ↑ main ↑ HEAD 이 구조를 이해하면 브랜치, checkout, reset이 훨씬 쉬워진다. 과거 버전 확인하기: checkout과 switch 예전부터 널리 사용된 명령은 git checkout 이다. git checkout <commit-id> 특정 과거 커밋으로 이동하면 HEAD가 브랜치가 아닌 커밋 자체를 직접 가리키는 Detached HEAD 상태가 될 수 있다. 요즘 Git에서는 역할을 더 명확하게 나누기 위해 브랜치 이동에는 git switch , 파일 복구에는 git restore 를 사용할 수 있다. git switch main 특정 커밋을 단순히 확인하려면 다음처럼 명시적으로 사용할 수 있다. git switch --detach <commit-id> 초보 단계에서는 다음 차이만 기억해도 충분하다. git switch main → main 브랜치로 이동 git switch --detach <commit> → 특정 과거 커밋을 임시로 확인 과거 상태를 둘러본 뒤 작업을 이어갈 때는 다시 브랜치로 돌아온다. git switch main reset은 HEAD가 가리키는 브랜치를 이동시킨다 switch 또는 checkout 이 현재 바라보는 위치를 바꾸는 것 이라면, reset 은 현재 브랜치 자체의 위치를 이동시키는 데 사용한다. A ── B ── C ── D ↑ main 다음 명령을 실행했다고 해보자. git reset --hard B 그러면 개념적으로는 다음처럼 된다. A ── B ── C ── D ↑ main C , D 가 즉시 물리적으로 사라진다고 생각하기보다는 main이 더 이상 해당 커밋을 가리키지 않게 됐다 고 이해하는 편이 정확하다. --hard 는 특히 조심해야 한다 git reset --hard <commit-id> --hard 는 브랜치 위치뿐 아니라 Working Directory와 Staging Area도 해당 커밋 상태로 맞춘다. 따라서 커밋하지 않은 변경사항이 사라질 수 있다. 실수한 상황에서 의미를 모르고 반복해서 reset, checkout, force push를 실행하기보다 현재 상태를 먼저 확인하는 것이 중요하다. reflog는 내가 이동한 기록까지 보여준다 커밋 로그에서 보이지 않게 된 커밋도 reflog 를 통해 다시 찾을 수 있는 경우가 많다. git reflog 예를 들어 실수로 reset을 했다면 이전 HEAD 위치가 reflog에 남아 있을 수 있다. abc1234 HEAD@{0}: reset: moving to HEAD~2 def5678 HEAD@{1}: commit: 결제 기능 수정 이전 커밋 ID를 확인한 뒤 필요하면 복구할 수 있다. git reset --hard def5678 Git이 안전하다는 말은 "아무 명령이나 실행해도 괜찮다"는 뜻이 아니다. 커밋과 참조 이동 기록을 잘 이해하면 복구 가능한 경우가 많다 는 의미에 가깝다. 공유한 히스토리는 함부로 다시 쓰지 않는다 로컬에서 아직 나만 사용하는 커밋이라면 reset이나 amend를 비교적 자유롭게 사용할 수 있다. 하지만 이미 원격 저장소에 push해서 다른 사람과 공유한 커밋은 상황이 다르다. 히스토리를 다시 쓴 뒤 다음과 같은 강제 push를 하면 다른 사람의 작업과 충돌할 수 있다. git push --force 협업 저장소에서는 일반적으로 강제 push를 최소화하고, 정말 필요하다면 팀 규칙을 먼저 확인하는 편이 안전하다. 불가피하게 강제 갱신이 필요할 때는 --force-with-lease 가 상대적으로 안전한 선택이 될 수 있지만, 이것도 히스토리를 변경하는 명령이라는 점은 같다. 기본 흐름을 한 번에 정리하기 새로운 프로젝트에서 가장 기본적인 작업 흐름은 다음과 같다. # 저장소 초기화 git init # 현재 상태 확인 git status # 파일 수정 후 스테이징 git add README.md # 버전 생성 git commit -m "README 작성" # 히스토리 확인 git log --oneline --graph --decorate 이 흐름을 반복하면 프로젝트의 변경 이력이 차곡차곡 쌓인다. 수정 ↓ git add ↓ Staging Area ↓ git commit ↓ 새로운 버전 Git 명령을 전부 암기하는 것보다 어떤 영역이 어떻게 이동하는지 를 이해하는 것이 훨씬 중요하다. 핵심 정리 Git은 분산 버전 관리 시스템 이고 GitHub는 Git 저장소를 호스팅하는 별도의 서비스다. git init 을 실행하면 프로젝트를 관리하는 .git 디렉터리가 생성된다. Git의 기본 흐름은 Working Directory → Staging Area → Repository 다. git add 는 다음 커밋에 포함할 변경을 선택하고, git commit 은 그 변경을 하나의 버전으로 만든다. 각 커밋에는 고유한 Commit ID가 있고 커밋 정보가 바뀌면 ID도 달라진다. HEAD 는 현재 바라보는 브랜치 또는 커밋을 나타낸다. git reset --hard 는 커밋하지 않은 변경을 잃을 수 있으므로 의미를 이해하고 사용해야 한다. git reflog 는 HEAD 이동 기록을 확인해 실수한 reset 등을 복구할 때 유용하다.
"이 후기는 구름 부트캠프에서 제공한 소정의 원고료를 받고 작성된 후기입니다." 9/1~9/30 좋았던 점 9월 한 달이라는 시간 동안 이전에 다루었던 이론 수업들을 한결 편안한 마음으로 복습할 수 있었다. 본래 석 달에 걸쳐 배웠던 내용이기에, 수료 직후 두 달과 앞으로 남은 네 달을 활용해 천천히 내 것으로 다져나갈 생각이다. 수료 이후에도 계속해서 구직 정보와 취업 케어를 제공받을 수 있었다는 점이 정말 좋았다. 특히 구름 부트캠프 수료생들에게만 별도로 연결되는 독점적인 구직 기회가 존재한다는 것이 매우 커다란 장점으로 느껴졌다. 수료한 지 한 달이 넘은 지금까지도 디스코드나 안내 공지방에 계속 접속할 수 있어, 필요한 정보를 언제든 얻을 수 있다는 사실 역시 마음을 든든하게 해주었다. 단순히 교육으로 끝나는 것이 아니라, 혼자 취업을 준비하는 기간 동안에도 끊임없이 필요한 소식과 도움을 받을 수 있는 창구가 열려 있어 매우 유용했다. 아쉬웠던 점 여전히 혼자 다루어야 하는 이론 분야는 내용이 꽤나 알차고 깊이가 있어, 완벽히 이해하고 내재화하기까지 조급하다는 느낌을 받았다. 수료를 마친 후에는 도움을 주시던 퍼실리테이터님도 없고 원래 따로 질문 게시판도 없다 보니, 궁금한 점을 해소하는 데 더 오랜 시간이 걸리기도 했다. 정규 교육이 진행되는 중간중간에 지난 커리큘럼을 다시금 되짚어보고 점검할 수 있는 여유 시간이 조금 더 확보되었더라면 한층 더 완성도 높은 배움이 되었을 것이라는 아쉬움이 남는다. 나처럼 속도가 다소 더딘 수강생을 위한 별도의 장치가 마련되었으면 더 좋았겠지만, 수료 후에도 이 정도로 배려해 주시는 것만으로도 감사한 부분이 훨씬 크다. 후기 전체적인 과정을 돌이켜보면 이번 기회는 단순한 지식 전달을 넘어, 개발자로서 스스로 고민하며 자립할 수 있는 단단한 토대를 만들어 준 뜻깊은 여정이었다. 과정이 마무리된 지금 시점에서도 배운 내용을 기반으로 꾸준히 자기개발을 이어갈 수 있는 동기부여를 계속해서 얻고 있다. 더불어 지속적으로 전달되는 취업 정보 덕분에 앞으로도 얼마든지 계속해서 성장할 수 있다는 희망을 품게 된다. 지금은 비록 어렵고 벅차게 느껴질지라도 차근차근 단계별로 나아가 내년에는 바이브 코딩을 본격적으로 배울 수 있는 구름에서 신설된 ai 특강도 수강해보고 싶다.
9월의 마지막 오늘은 프로그래머스에 코딩 기초 트레이닝이란게 있어서 해봤다. 문제 설명 : 문자열 str 이 주어질 때, str 을 출력하는 코드를 작성해 보세요. 제한사항 : 1 < str의 길이 < 1,000,000 str에는 공백이 없으며, 첫째 줄에 한 줄로만 주어집니다. 1 import java.util.Scanner; 2 3 public class Solution 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 5 풀이 : 1 import java.util.Scanner; 2 3 public class Solution 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 5 6 System.out.println(sc.nextLine()); 7 } 8} 오늘도 내가 풀지 못했다 처음에는 scanner 다 지우고 그냥 str에 넣어서 풀었는데 당연하게도 틀렸다 많은 풀이들이 있었는데 이게 깔끔해 보여서 좋은 코드 같다.
백엔드·인프라 개념 1 배포 인프라를 고르다 보면 마주치는 IaaS/PaaS/BaaS 같은 용어부터, AWS/GCP/Azure 같은 클라우드, 그리고 쿠버네티스/데브옵스까지 — 처음엔 다 비슷해 보이는데 정리해두면 확실히 구분됨. IaaS / PaaS / SaaS / BaaS — "얼마나 직접 관리하는가"의 스펙트럼 모델 정체 내가 관리하는 것 제공자가 관리하는 것 비유 IaaS 인프라(서버, 네트워크)만 임대 OS, 런타임, 앱, 데이터 전부 물리 서버, 네트워크, 가상화까지만 빈 땅 임대 — 건물은 내가 지음 PaaS 개발 플랫폼 자체를 제공 코드, 데이터만 OS, 서버, 배포 파이프라인까지 인테리어까지 된 상가 임대 SaaS 완성된 소프트웨어를 그냥 사용 아무것도 안 함 (사용만) 전부 다 완성된 프랜차이즈 매장 BaaS 백엔드 기능(DB, 인증, 스토리지)만 API로 제공 프론트엔드 코드, 비즈니스 로직 일부 DB 서버, 인증 시스템, 스토리지 인프라 주방을 통째로 빌려주는 것 실제 서비스에 대입하면: AWS EC2 = IaaS (가상 서버만 받고 나머지 다 직접 설치) Vercel = PaaS (Git push만 하면 배포 끝, 서버 관리 없음) Supabase, Firebase = BaaS (DB+인증+스토리지를 API로 바로 사용) GCP, Azure, Oracle Cloud = IaaS/PaaS를 다 포함하는 종합 클라우드 AWS/GCP/Oracle 같은 종합 클라우드는 그 안에 IaaS도 있고 PaaS도 있고 BaaS급 관리형 서비스(RDS 등)도 다 있음. Vercel/Supabase는 그중 특정 레이어만 딱 잘라 쉽게 만든 전문 서비스. 배포 인프라 종류 비교 구분 대표 서비스 특징 난이도 PaaS (프론트/풀스택 특화) Vercel, Netlify, Railway, Render Git push만 하면 자동 배포 매우 쉬움 BaaS Supabase, Firebase DB+인증+스토리지 관리형 제공 쉬움 클라우드 종합 AWS, GCP, Azure 확장성/커스터마이징 최강, 대신 직접 설정할 게 많음 어려움 컨테이너 기반 Docker + Kubernetes 대규모 MSA에 적합 매우 어려움 전통 VPS 자체 서버 서버를 통째로 빌려서 직접 세팅 어려움 3대 종합 클라우드: AWS / GCP / Azure (+ Oracle) [종합 클라우드 - IaaS/PaaS 다 포함] AWS --- GCP --- Azure --- Oracle Cloud (점유율 1위) (AI/데이터 강점) (기업통합 강점) (DB 전통 강자) Azure Microsoft의 종합 클라우드, AWS/GCP와 동급인 3대장 Windows Server, Active Directory, .NET, Office 365 같은 Microsoft 생태계 통합이 강점 한국에서는 금융권/대기업/공공기관 쪽에서 AWS 다음으로 많이 쓰임 (Java+SI형 시장과 자주 겹침) AWS와 비슷한 구조의 가입 크레딧, 12개월 무료 티어, Always Free 서비스가 있음 Cloudflare 원래는 CDN + DNS + 보안(DDoS 방어) 전문 회사 최근엔 Workers(서버리스 함수), Pages(정적 사이트 호스팅), D1(DB), R2(스토리지)로 PaaS/BaaS 영역까지 확장 중 강점은 전 세계 엣지 네트워크 — "어디서 접속해도 빠름"이 핵심 무기 무료 티어가 넉넉한 편으로 유명함 (Vercel보다 후하다는 평가도 있음) Vercel과 비슷한 포지션으로 최근 경쟁 구도 (Cloudflare Pages vs Vercel) Firebase vs Supabase 둘 다 BaaS지만 내부 구조가 다름. Firebase Supabase DB 종류 NoSQL (Firestore) PostgreSQL (SQL) 오픈소스 아님 (Google 종속) 오픈소스 SQL 지식 활용 안 됨 SQL 지식 그대로 활용 가능 데이터 마이그레이션 Google 생태계에 묶임 Postgres라 다른 곳으로 이전 쉬움 Firebase는 실시간 DB, 인증, 푸시 알림, 호스팅까지 올인원으로 제공하는 Google의 BaaS로, Supabase의 원조격 존재. 둘 다 무료 티어(Firebase는 Spark 요금제)가 있음. SQL vs NoSQL — Firebase/Supabase 선택의 핵심 기준 SQL (MySQL, PostgreSQL 등) NoSQL (Firestore 등) 데이터 구조 표(Table) - 행/열로 정형화 문서(Document) - JSON처럼 자유로운 구조 스키마 미리 정의해야 함 스키마 없음, 문서마다 필드 달라도 됨 관계 표현 JOIN으로 테이블 간 관계 연결 관계 개념 자체가 약함 쿼리 언어 SQL (표준화됨) 제품마다 다른 API 적합한 경우 데이터 간 관계가 명확하고 정합성이 중요할 때 구조가 자주 바뀌거나 대용량 실시간 데이터 처리 MySQL과 PostgreSQL은 둘 다 SQL 계열이라 문법이 거의 비슷해서 갈아타는 데 큰 무리가 없음. 반면 NoSQL(Firestore)은 아예 다른 사고방식이라 별도로 새로 배워야 하는 개념에 가까움. 쿠버네티스 · 데브옵스 · 클라우드 네이티브 — 하나의 흐름 클라우드 네이티브 (철학/목표) "클라우드에 최적화된 방식으로 서비스를 만들자" ↓ 컨테이너(Docker) + 쿠버네티스 (도구) "서비스를 컨테이너 단위로 쪼개고, 자동으로 관리하자" ↓ 데브옵스 (문화/프로세스) "개발자가 배포·운영까지 같이 책임지고, CI/CD로 빠르게 순환시키자" 쿠버네티스(K8s) : 컨테이너를 수십~수백 개 띄워야 할 때 자동으로 배치·확장·복구해주는 오케스트레이션 도구. 대규모 서비스(Netflix급)에서 진가를 발휘하고, 개인/소규모 프로젝트에는 사실상 오버스펙. 서버리스(Vercel 등)는 애초에 컨테이너 관리를 신경 안 써도 되게 만든 방식이라 K8s가 하는 일 자체가 필요 없어짐. 데브옵스 : 핵심 도구는 CI/CD(GitHub Actions, Jenkins 등) — 코드 push하면 자동 테스트·빌드·배포되는 파이프라인. Vercel에 GitHub 연동해서 push하면 자동 배포되는 것 자체가 CI/CD의 가장 간단한 형태. 클라우드 네이티브 : 특정 기술이 아니라 방향성/철학. "이런 관점이 있다" 정도로 알아두면 충분한 개념.
문제 링크 섬 연결하기 코드 #include <string> #include <vector> #include <algorithm> using namespace std; int parent[100]; int findParent(int a) { if (parent[a] == a) { return a; } return parent[a] = findParent(parent[a]); // 최적화 } void unionParent(int a, int b) { int pa = findParent(a); int pb = findParent(b); if (pa != pb) { if (pa < pb) { parent[pb] = pa; } else { parent[pa] = pb; } } } bool cmp(const vector<int>& a, const vector<int>& b) { return a[2] < b[2]; } int solution(int n, vector<vector<int>> costs) { int answer = 0; // 최소 신장 트리(MST) -> 크루스칼 알고리즘 사용 // 유니온 사용: findParent(), unionParent() // 1. 간선 가중치를 오름차순으로 정렬 // 2. 크루스칼 알고리즘: 간선을 하나 꺼내서 노드들을 유니온 sort(costs.begin(), costs.end(), cmp); for (int i = 0; i < n; i++) { // 부모 테이블 초기화 parent[i] = i; } int cnt = 0; // 현재 사용한 간선 개수 for (const auto& edge : costs) { int u = edge[0]; int v = edge[1]; int w = edge[2]; if (findParent(u) != findParent(v)) { unionParent(u, v); answer += w; cnt++; } if (cnt == n - 1) { break; } } return answer; } 회고 findParent() 에서 return parent[a] = findParent(parent[a]) 로 parent[a] 를 바로 갱신하여 최적화시킬 수 있다. unionParent() 에서 a, b 자체가 아니라 그 부모를 갱신해야 한다. 한 그룹의 대장을 바꿔야 제대로 합쳐지기 때문이다. 예전에는 우선순위 큐로 정렬 효과를 누렸는데, 이번에는 벡터를 가중치 기준으로 오름차순 정렬했다. cmp 의 매개변수를 벡터로 설정하고 특정 원소끼리 비교할 수 있는 것도 처음 알았다. cnt 를 사용하여 사용한 간선 수가 n-1 이 되면 멈추도록 했는데, 최적화 코드이기 때문에 이 문제에선 없어도 된다. 하지만 혹시 몰라 넣었다.