때는 행동대장 프로젝트를 진행하면서…
새로운 이슈를 처리하기 위해 원격에서 만들어둔 브랜치를 로컬로 가져오려고 시도했다. 그러나 만들어진 브랜치의 마지막 log를 확인해보니 원격 브랜치의 log와 달랐다! 나는 원격 브랜치를 그대로 로컬로 가져오는 방법을 검색했고.. git의 checkout을 통해 성공할 수 있었다.
도대체 이 checkout이 무엇을 수행하는 명령어이길래 원격 브랜치를 그대로 로컬에 생성할 수 있는지 알아보도록 하자. 그리고 checkout과 유사한 일을 수행하는 switch와 restore은 무엇인지도 같이 알아보자!
✅ checkout?
checkout 명령어는 브랜치 변경, 특정 커밋 체크아웃, 파일 복원 등 여러 작업에 사용된다.
checkout으로 할 수 있는 기능
1. 브랜치 변경
현재 작업 중인 브랜치를 다른 브랜치로 변경하는 데 사용된다.
git checkout <branch-name>
- 현재 브랜치를
branch-name
으로 변경한다.
- 작업 디렉토리는 변경된 브랜치의 마지막 commit 상태로 업데이트된다.
- 단, commit 혹은 stash하지 않은 작업이 존재할 경우(즉, working directory에 존재) 다른 브랜치로 변경할 수 없다.
2. 새 브랜치 생성 및 이동
브랜치를 생성하고 바로 이동할 때 사용한다.
git checkout -b <new-branch-name>
- 새로운 브랜치를 생성하고 해당 브랜치로 이동한다.
-
만약 원격 브랜치를 추적하는 로컬 브랜치를 생성하고 이동하고 싶을 경우 다음과 같다.
git checkout -b <new-branch-name> <remote>/<remote-branch-name>
or
git checkout --track <remote>/<remote-branch-name>
예로, origin에 있는 feature1이라는 브랜치를 로컬에 바로 생성하고 이동하고 싶다면 다음과 같다.
git checkout -b feature1 origin/feature1
or
git checkout --track origin/feature1
origin의 브랜치 이름과 다른 이름을 사용할 수 있다. feature1이 아니라 feat1로 만들고 싶다면
git checkout -b feat1 origin/feature1
3. 특정 커밋 체크아웃
특정 커밋으로 working directory를 업데이트한다.
git checkout <commit-hash>
commit-hash
로 지정된 커밋의 파일들을 작업 디렉토리에 반영한다.
- 이 상태는 “detached HEAD” 상태라고 불리며, 이후 작업은 브랜치가 아닌 해당 커밋을 기준으로 진행된다.
4. 파일 복원
특정 파일을 이전 커밋의 상태로 복원한다.
git checkout <branch-name> -- <file-path>
<file-path>
에 있는 파일을 지정된 브랜치 또는 커밋의 상태로 되돌린다.
- 이 명령은 주로 잘못된 수정 내용을 되돌릴 때 유용하다.
checkout의 기능이 분리?
Git 2.23 이후, checkout
의 일부 기능이 switch
(브랜치 변경)와 restore
(파일 복원)로 분리되었다. 각각의 명령은 특정 작업에 초점이 맞춰져 있어 더 직관적이고 명확하게 사용할 수 있다. 그렇지만 checkout
은 여전히 사용 가능하다.
하지만 기능이 분리되었기 때문에, 이제는 switch와 restore를 사용하는 것을 더 권장한다. (아무래도 checkout은 책임 분리가 덜 된 구조였기 때문인 듯 하다.)
그렇다면 checkout
에서 사용하던 기능을 switch와 restore로 어떻게 대체할 수 있는지 알아보자!
🕹️ switch
브랜치를 이동하거나 새로 생성하는 작업에만 사용된다. checkout
이 한꺼번에 처리하던 다양한 작업 중 브랜치와 관련된 작업만 분리한 명령이다.
주요 기능
1. 브랜치 전환
현재 작업 중인 브랜치를 <branch-name>
으로 변경한다.
2. 새 브랜치 생성 및 이동
새 브랜치를 생성하고 해당 브랜치로 이동한다.
git switch -c <new-branch-name>
3. 강제 전환
작업 중인 변경 사항을 무시하고 강제로 브랜치를 변경한다.
git switch -f <branch-name>
♻️ restore
특정 파일을 이전 상태로 복원하는 작업에 초점을 맞춘 명령이다. 이는 checkout
의 파일 복원 기능을 대체한다.
주요 기능
1. 작업 디렉토리에서 파일 복원
스테이징된 변경 사항이나 working directory의 변경 사항을 마지막 커밋 상태로 되돌린다.
2. 스테이징 영역에서 복원
스테이징 영역(Staging Area)에 있는 파일을 언스테이징한다.
git restore --staged <file-path>
3. 특정 커밋 기준으로 파일 복원
<commit-hash>
에 해당하는 커밋 상태로 파일을 복원한다.
git restore --source=<commit-hash> <file-path>
🥊 checkout vs switch & restore
기능 |
checkout |
switch |
restore |
브랜치 변경 |
O |
O |
X |
새 브랜치 생성 및 이동 |
git checkout -b <branch> |
git switch -c <branch> |
X |
파일 복원 |
git checkout <branch> -- <file-path> |
X |
git restore <file-path> |
스테이징 영역 복원 |
git checkout -- <file-path> |
X |
git restore --staged <file-path> |
특정 커밋 기반 파일 복원 |
git checkout <commit-hash> -- <file> |
X |
git restore --source=<commit-hash> |
📝 정리
checkout
: 브랜치 변경, 특정 커밋 체크아웃, 파일 복원 등 여러 작업에 사용.
switch
: 브랜치를 전환하거나 생성하는 데만 사용.
restore
: 파일 복원 및 스테이징 관련 작업에 사용.
switch
와 restore
는 Git 2.23 이상에서만 사용할 수 있다. 구버전에서는 checkout
을 사용해야 한다.
switch
와 restore
를 사용하면 작업의 목적과 의도가 명확히 드러나므로, 사용을 권장한다.
📚 참고
문제 발생 상황
때는, 행동대장 회원 탈퇴 기능 구현에서 google spreadsheets api를 사용하기 위해서 google-auth-library를 활용하여 코드를 작성하던 중.. 이런 어마무시한 에러의 늪에 빠져버렸다.

이 에러가 무슨 에러인데?
webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
위 에러를 해석하면 다음과 같다.
이 에러 메시지는 Webpack 5부터 Node.js의 코어 모듈(예: http, https, fs, 등)에 대한 폴리필(polyfill)이 기본적으로 제공되지 않는다는 것을 의미함.
Webpack 4까지는 브라우저 환경에서도 Node.js 코어 모듈이 동작할 수 있도록 자동으로 폴리필을 포함했다. 그러나 Webpack 5부터는 이를 자동으로 포함하지 않으며, 사용자가 필요한 폴리필을 직접 설정해야 하도록 바뀌었다. 우리 행동대장의 Webpack의 버전은 ^5.93.0
… 그렇기에 폴리필을 직접 설정을 해줘야 해당 에러가 해결된다.
근데 폴리필이 뭐예요?
폴리필(Polyfill)은 최신 기술이나 특정 기능이 일부 환경에서 지원되지 않을 때, 그 기능을 대신 구현하여 호환성을 제공하는 코드나 라이브러리를 말한다.
쉽게 말해, 오래된 브라우저나 특정 환경에서도 새로운 기능을 사용할 수 있도록 도와주는 “백업” 코드이다.
그렇다면 폴리필이 왜 필요할까?
JavaScript는 빠르게 진화하지만, 모든 브라우저나 환경이 새로운 기능을 지원하는 것은 아니다.
예를 들어 Array.prototype.includes
는 최신 브라우저에서는 지원되지만, 구형 브라우저(ex, IE)에서는 작동하지 않는다. 이 경우, 폴리필을 사용하면 Array.prototype.includes
와 동일한 기능을 추가할 수 있다.
Webpack에서 폴리필은 어떻게 설정해야 하죠?
Webpack은 브라우저 환경에서 동작하도록 설계되었지만, 일부 Node.js 기능을 사용할 때 폴리필이 필요하다. 그렇기에 필요한 Node.js 코어 모듈(http
, fs
등)을 직접 설치하고 설정해주면 되겠다!
에러 해결
아래 이미지 외에도 많은 폴리필 에러가 발생했다.. 에러 메세지를 확인하면 설치해야 할 라이브러리를 알려주고 있어 설치하라는 라이브러리를 모두 설치하고 설정해주었다.

- 에러 메시지의 라이브러리
- url
- crypto-browserify
- stream-http
- https-browserify
- os-browserify
- stream-browserify
- path-browserify
- querystring-es3
- assert
위 라이브러리를 설치한 후 webpack 파일의 resolve.fallback
에 값을 설정해줬다.
또한 추가로 reqire를 사용할 수 있도록 createRequire를 생성했다.
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
export default {
// ...
resolve: {
fallback: {
url: require.resolve('url'),
fs: false,
crypto: require.resolve('crypto-browserify'),
http: require.resolve('stream-http'),
https: require.resolve('https-browserify'),
os: require.resolve('os-browserify/browser'),
stream: require.resolve('stream-browserify'),
path: require.resolve('path-browserify'),
querystring: require.resolve('querystring-es3'),
assert: require.resolve('assert/'),
},
// ...
};
결과
아래 이미지를 통해 폴리필과 관련된 에러가 모두 사라졌음을 확인할 수 있다! (근데 난 다른 에러가 남았지!)

참고
에러
폴리필 에러를 해결했지만 아직도 우리에겐 에러가 남았다.. 에러 내용은 다음과 같다.

에러 해석
Module not found: Error: Can't resolve 'child_process' in '/Users/soha/soha/wooteco/2024-haeng-dong/client/node_modules/google-auth-library/build/src/auth’
Module not found
: 특정 모듈을 찾지 못하겠다.
Error: Can't resolve 'child_process'
: Node.js의 내장 모듈인 child_process
를 찾을 수 없다는 뜻. 이 모듈은 일반적으로 서버 환경(Node.js)에서 사용되며, 브라우저 환경에서는 사용할 수 없음…..
in '/Users/soha/soha/wooteco/2024-haeng-dong/client/node_modules/google-auth-library/build/src/auth'
: 에러가 발생한 위치는 google-auth-library
패키지 내부의 코드에서 child_process
를 사용하려고 시도한 부분임.
종합하자면?
google-auth-library
는 Google API 인증을 위해 사용되는 라이브러리이다. 이 라이브러리는 서버 환경(Node.js)을 위해 설계되었으므로, 브라우저 환경에서 사용하려고 하면 child_process
와 같은 Node.js 전용 모듈을 찾지 못해 에러가 발생한다!
결론
너! Node.js에서 실행되어야 하는거 왜 브라우저에서 하셈? 그거 브라우저에서는 실행 못하니 돌아가.
🚨 해당 issue는 구현에 실패했으며, 해당 PR은 실패 사유에 대해 공유하는 글입니다.
구현 목적
회원 탈퇴 기능을 왜 구현해야 하죠?
행동대장 서비스는 카카오로 회원가입 및 로그인이 도입되면서, 회원 탈퇴에 대한 기능이 필수적으로 필요하게 되었습니다(개인정보보호법). 따라서 회원 탈퇴 기능을 구현하게 되었습니다. 그리고 이전 이슈에서 언급한 것처럼 회원가입을 진행한 유저가 탈퇴를 하는 사유에 대해 트래킹을 해야 한다고 생각했습니다. 탈퇴 사유를 수집함으로 우리 서비스의 문제를 보완해 나갈 수 있기 때문입니다.
구글 스프레드시트를 데이터베이스로 사용한 이유
탈퇴 사유를 백엔드의 api를 통해 저장할 수 있지만, 저희에게는 현재 서버 비용에 한계가 존재합니다. 서비스를 통해 금전적인 이득을 취하는 상황이 아니기 때문이죠. 그렇기에 회원 탈퇴 이용에 큰 영향을 끼치지 않는 선에서 서비 비용을 줄여야 한다고 생각했습니다. 이렇게 생각하여 찾은 방법이 구글의 스프레드시트 api를 활용하는 것이었습니다.
구글 스프레드시트를 데이터베이스로 활용하여 회원 사용자의 탈퇴 사유를 저장합니다. 구글 스프레드시트는 잘 만들어진 서비스로 우리가 추후 탈퇴 사유에 대한 데이터를 분석하고 싶을 때 쉽게 확인할 수 있을 것입니다.
구현 방법
해당 파트는 이런 프로세스로 구글 스프레드시트를 활용할 수 있음을 정리한 것입니다. 아래 설명 할 방법처럼 구현을 하려고 했으나 제 판단으로는 프론트엔드만으로는 구현이 불가능하였다는 점..
구글 spreadsheet api 사용을 위한 단계
- 구글 클라우드 프로젝트 생성 및 서비스 계정 생성
- 스프레드 시트에 접근할 수 있도록 구글 계정 인증
- http 요청
1. 구글 클라우드 프로젝트 생성
- 구글 클라우드에서 프로젝트를 생성합니다.
- 해당 프로젝트의 API에 Google Sheets를 추가합니다.
- 서비스 계정을 생성합니다.
- 계정 생성으로 얻게된 credential 파일을 클라이언트 폴더에 추가합니다.
해당 파일에는 구글 스프레드시트에 접근하고 편집 가능할 수 있도록 하는 다양한 인증 id, key 등이 존재합니다.
- 구글 스프레드시트에서 연결하고자 하는 시트를 생성합니다.
- 해당 시트에 편집자로 서비스 계정 생성을 통해 얻은 이메일을 등록하고 편집자 권한을 부여합니다.
- 해당 시트의 url에서 sheet id를 가져와 클라이언트 파일에서 사용할 수 있도록 저장해둡니다.
2. 스프레드 시트에 접근 | 두가지 방법 (근데 에러를 곁들인..)
스프레드 시트에 접근하기 위해서 다음 범위(scope) 중 하나가 필요합니다.
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/spreadsheets
우리는 위에 언급한 범위를 승인 받기 위해서 아까 위에서 생성된 서비스 계정의 이메일로 접근해야 합니다. 이때 우리는 이 승인을 각각 다른 라이브러리를 활용하여 진행할 수 있습니다.
googleapis 라이브러리 활용
- googleapis 라이브러리에서 google import
- google.auth를 사용하여 인증
google-auth-library 활용
- google-auth-library에서 JWT import
- new JWT를 생성하여 인증
- 단, 해당 라이브러리를 사용하여 구글 스프레드시트에 인증을 받을 때는 google-spreadsheet 라이브러리를 활용합니다.
google-spreadsheet는 외부에서 제작한 라이브러리이기 때문에 변화에 즉각 대응하기 어렵습니다. 하지만 비교적 구현하기 쉽습니다.
🚨두 방법 모두 공통적인 문제 발생
두가지 방법을 각각 시도하면서 공통적인 문제가 발생했습니다. 무려 에러의 갯수는 총 35개…

에러의 유형은 다음과 같습니다.
- webpack 버전 5 사용으로 인해 폴리필 에러
webpack < 5 used to include polyfills for node.js core modules by default
- child_process를 찾을 수 없는 에러
Module not found: Error: Can't resolve 'child_process’
- net, tls를 찾을 수 없는 에러
Module not found: Error: Can't resolve 'net’
폴리필 에러는 해결이 가능했습니다만… 남은 두 에러는 해결이 불가능했습니다..
위의 두 에러는 Node.js 환경에서 동작해야 할 코드가 브라우저 환경에서 실행하려고 할 때 발생하는 것이기 때문이었죠..
(이 에러를 발견한 상황을 보고 싶다면 여기를 클릭..)
네.. 저는 클라이언트에서 서버 코드를 실행하고 있었던 것이었스빈다!!ㅜ
👉결론👈
구글 스프레드시트 api를 사용하고 싶으면 백엔드가 필요하다… 입니다…
3. http 요청
해당 코드가 실행되면 403에러가 발생하는 것을 보아하니 제대로 된 코드는 맞는 것 같습니다. 아까워서… 이렇게라도 남겨봐요…
잘 작동하는지 테스트하기 위해서 값들은 냅다 넣었습니다. 흐린눈.. 부탁드립니다.
(참고로 아래 코드에서 사용한 requestPostWithResponse는 행동대장 클라이언트 코드에서 제작한 api 요청을 위한 메서드입니다.)
export const requestPostGoogleSpreadSheets = async () => {
await requestPostWithResponse({
baseUrl: 'https://sheets.googleapis.com/v4/spreadsheets',
endpoint: `/${process.env.GOOGLE_SPREADSHEET_ID}/values/Sheet1!A1:E1:append`,
queryParams: {
valueInputOption: 'RAW',
},
headers: {
'Access-Control-Allow-Credentials': 'true',
},
body: {
range: 'Sheet1!A1:E1',
majorDimension: 'ROWS',
values: [
['Door', '$15', '2', '3/15/2016'],
['Engine', '$100', '1', '3/20/2016'],
],
},
});
};
참고
때는 이번주 월요일. 행동대장 프로젝트에서 버그를 발견했다.
버그는 카카오 프로필 이미지를 연동하지 않았을 경우 회원 프로필 이미지가 alt로 출력되는 버그였다. 이 버그의 원인은 null 병합 연산자 ??
의 사용이었다. 나도 이전까지는 모르고 사용했던 연산자였다. 최근에 자바스크립트 딥 다이브를 읽고 정확한 개념을 알게 되었다.
나와 같이 null 병합 연산자가 삼항 연산자와 유사하게 동작할 것이라고 혼동했던 사람들이 존재할 것이라고 생각 된다. 그래서 이번에 null 병합 연산자가 무엇인지 정확히 알아보겠다!
null 병합 연산자
null 병합 연산자 (nullish coalescing) ??
는 좌항의 피연산자가 nullish 값일 경우 우항의 피연산자를 반환한다. nullish 값이 아닐 경우에는 좌항의 피연산자를 반환한다.
let soha = null;
const foo = () => soha ?? '없음';
console.log(foo()); // '없음'
soha = 'soha'; // soha로 재할당
console.log(foo()); // 'soha'
null 병합 연산자의 경우에는 논리 연산자 ||
를 사용하여 단축 평가를 했을 때 보다 예기치 않은 동작을 방지할 수 있어서 좋다. 논리 연산자 ||
의 경우에는 좌항의 피연산자가 falsy 값일 경우에 우항의 피연산자를 반환하기 때문이다.
let soha = '';
const foo = () => soha ?? '없음';
console.log(foo()); // ''
const boo = () => soha || '없음';
console.log(boo()); // '없음'
자, 위 설명에서 nullish와 falsy라는 용어가 나왔다. nullish와 falsy는 무엇일까?
nullish
nullish는 값이 null
또는 undefined
인 경우를 말한다.
nullish 값
null
: 값이 “비어 있음”을 명시적으로 나타내는 값
- 프로그래머가 의도적으로 값이 없음을 지정할 때 사용.
- 객체의 기본 값이나 초기화되지 않은 상태를 나타낼 수 있음.
undefined
: 값이 “정의되지 않음”을 나타내는 값
- 선언된 변수에 값이 할당되지 않았을 때 기본적으로 설정되는 값.
- 함수가 명시적으로 반환값을 설정하지 않을 때도 반환되는 값.
falsy
falsy는 조건문에서 “거짓처럼 평가되는 값”을 말한다.
falsy 값
false
: 불리언 타입의 거짓 값
0
: 숫자형 타입의 0
-0:
숫자형 타입의 음수 0
0n
: BigInt 타입의 0
""
: 문자열 타입의 빈 값
null
undefined
NaN
: 숫자 연산이 실패했음을 나타내는 특수 숫자 값
- “Not a Number”(숫자가 아님)의 약자.
- 숫자형 타입이지만 계산 불가능한 값이나 잘못된 숫자 연산의 결과를 나타냄.
ex)
0 / 0
, parseInt("abc")
.
다시 보기
nullish와 falsy가 무엇인지 알았으니 위에서 설명했던 코드를 다시 보자.
let soha = '';
const foo = () => soha ?? '없음';
console.log(foo()); // ''
const boo = () => soha || '없음';
console.log(boo()); // '없음'
null 병합 연산자는 soha의 값이 빈 문자열(falsy)이기 때문에 soha 값(’’
)을 출력했다.
논리 연산자 ||
는 soha의 값이 falsy이기 때문에 ‘없음’
을 출력했다.
참고