git version 2.41.0.windows.1을 기준으로 작성
git init
깃(git)을 사용하기 위한 명령어로 .git 디렉토리가 생성된다.
현재 프로젝트에서 변경사항 추적(버전 관리)을 시작.
git config
깃(git)을 사용하기 위한 설정 값을 입력, 확인할 수 있다.
개행문자(new line) 설정은 내 깃허브(github)에 올린 소스코드를 다른 사람이 사용할 때 내 운영체제와 다르면 개행 문자 때문에 오류가 발생할 수 있는 것을 방지한다.
### 현재 프로젝트에만 설정하는 경우
# 이름, 이메일을 지정
git config user.name <'사용자 이름'>
git config user.email <'사용자 이메일'>
# 개행문자 설정
git config core.autocrlf input # mac
git config core.autocrlf true # windows
# 이름, 이메일을 확인
git config user.name
git config user.email
### 전역적으로 설정하는 경우
# 이름, 이메일을 지정
git config --global user.name <'사용자 이름'>
git config --global user.email <'사용자 이메일'>
# 이름, 이메일을 확인
git config --global user.name
git config --global user.email
# 개행문자 설정
git config --global core.autocrlf input # mac
git config --global core.autocrlf true # windows
### 설정 값을 한 번에 확인
git config --list
git config -l
git add
버전으로 만들기 위한 파일을 추가한다.(변경사항을 추적할 파일을 지정한다.)
파일을 추가하는 것을 [ 스테이징한다, 인덱싱한다 ] 라고 한다.
add명령어를 사용하여 추가된 파일들을 모아두는 곳을 스테이징 영역(staging area)이라 하며 인덱스(indext)라고도 한다.
# 특정 파일을 추가
git add <파일명.확장자>
# 모든 파일을 추가
git add .
git commit
스테이징 된 파일들을 버전으로 만들고, 버전을 설명하는 메세지를 작성할 수 있다.
버전으로 만들어진 파일은 변경사항이 추적이 된다.
# 버전을 만들고 버전에 대한 메세지를 작성
git commit -m <'메세지'>
# 최신 커밋의 메세지를 새로운 메세지로 변경
git commit --amend -m <'새로운 메세지'>
# 커밋된 이력이 있는 파일은 -a 옵션을 통해 add 명령어를 대신할 수 있음
git commit -a -m <'메세지'>
git commit -am <'메세지'>
💡 트랙 파일(tracked file)
스테이징이 되거나, 커밋을 했던 파일들은 git에서 추적을 한다는 의미로 트랙 파일(tracked file)이라고 한다.
git remote
원격 저장소의 주소는 문자가 길기 때문에 별칭을 지정하여 프로젝트(로컬)와 원격 저장소를 서로 연결하여 github의 repository에 파일을 업로드할 수 있는 상태를 만든다.
웬만하면 origin이라는 별칭을 사용한다.
# 원격 저장소와 연결
git remote add <원격 저장소의 별칭> <원격 저장소의 주소>
# 원격 저장소 목록 확인
git remote
git remote -v # 자세히
git push
버전으로 만든 파일들을 연결된 원격 저장소에 업로드한다.
# 원격 저장소에 브랜치를 업로드
git push <원격 저장소의 별칭> <브랜치 이름>
# 원격 저장소의 모든 커밋 히스토리를 초기화하고 현재 로컬 저장소의 커밋을 강제로 업로드
git push -f <원격 저장소의 별칭> <브랜치 이름>
git push --force <원격 저장소의 별칭> <브랜치 이름>
# 로컬 저장소의 브랜치를 업로드할 원격 저장소의 브랜치를 지정
# 지정한 후에는 git push 명령어만으로도 원격 저장소의 해당 브랜치에 업로드가 가능
# ex) 로컬의 'develop'브랜치에서 git push를 하면 자동으로 '원격저장소별칭/develop'에 업로드
# 지장하면 git pull, git fetch에서도 적용된다
git push -u <원격 저장소의 별칭> <브랜치 이름>
git push --set-upstream <원격 저장소의 별칭> <브랜치 이름>
# 로컬에서 원격 저장소의 브랜치 삭제
git push -d <원격 저장소의 별칭> <브랜치 이름>
git push --delete <원격 저장소의 별칭> <브랜치 이름>
git status
워킹디렉토리(working directory, working tree)에서 파일의 현재 상태를 알 수 있다.
git status
Untracked files:
(use "git add <file>..." to include in what will be committed)
index.html
# 언트랙된 상태로써 변경사항을 추적할 수 없는 파일
# 즉, add 명령어로 스테이징을 하지 않은 상태의 파일이므로 커밋을 할 수 없음
git status
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: index.html
# index.html을 스테이징한 상태로 커밋을 할 수 있음
git status
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: index.html
# 트랙된 파일(=추적대상이 된 파일, 커밋한 이력이 있는 파일)에 변화가 생긴 상태
# 파일을 수정했을 경우에는 modified, 삭제했을 경우에는 delete라는 문구가 나타남
git log
커밋한 이력을 알 수 있다.
# 커밋 이력을 확인
git log
git log --oneline # 한 줄로
git log --stat # 자세히
git diff
최신 커밋의 파일들과 워킹디렉토리내의 파일들의 내용을 비교한다.
내용이 같을 경우에는 비교이력이 보이지 않는다.
git diff <비교 시점> <비교 대상> <비교 파일>
비교 시점 - HEAD(현재 브랜치의 최신 커밋, 생략가능), 커밋 ID, 로컬 브랜치, 원격 브랜치
비교 대상 - HEAD(현재 브랜치의 최신 커밋, 생략가능), 커밋 ID, 로컬 브랜치, 원격 브랜치, 파일명
비교 파일 - 파일명(비교 시점과 비교 대상 둘 다 커밋이거나 브랜치 일 때 사용. 특정 파일만을 비교)
비교 시점을 a 디렉토리로 표시. 비교 시점의 파일의 내용에 -(마이너스)를 표시하여 구분함
비교 대상을 b 디렉토리로 표시. 비교 대상의 파일의 내용에 +(플러스)를 표시하여 구분함
단, 커밋과 브랜치간의 비교는 불가
# 로컬 저장소의 최신 커밋의 파일과 워킹 디렉토리의 파일을 모두 비교
git diff
# a/a.txt와 b/a.txt에서
# a(슬러시 앞의 a)는 최신 커밋을 의미하고
# b(슬러시 앞의 b)는 워킹 디렉토리(작업 트리)를 의미함
# 즉 a/a.txt는 최신커밋/a.txt 라는 뜻이고
# b/a.txt는 워킹디렉토리/a.txt 라는 뜻
diff --git a/a.txt b/a.txt # a/a.txt는 로컬 저장소 최신 커밋의 a.txt를, b/a.txt는 워킹 디렉토리의 a.txt를 말함
index 8c46f44..bb736a6 100644 # 몰라도 되는 메타데이터
--- a/a.txt # 로컬 저장소의 파일의 내용은 -(마이너스)로 표시한다는 뜻
+++ b/a.txt # 워킹 디렉토리의 파일의 내용은 +(플러스)로 표시한다는 뜻
@@ -1,2 +1,3 @@
제품 : 컴퓨터
-가격 : 100만원 # 로컬 저장소의 파일의 내용
+가격 : 50만원 # 워킹 디렉토리의 파일의 내용
+사이즈 : 24인치 # 워킹 디렉토리의 파일의 내용
# 변경된 파일의 이름만 확인
git diff --name-only
# <비교 대상>을 스테이징된 파일로 지정
git diff --cached <비교 시점> <경로/파일명.확장자>
# 커밋 간의 비교
git diff <커밋 ID> <커밋 ID>
# 커밋 간의 특정 파일만을 비교
git diff <커밋 ID> <커밋 ID> <경로/파일명.확장자>
# 최신 커밋과 특정 파일만 비교
git diff <[HEAD]> <경로/파일명.확장자>
# 특정 커밋과 특정 파일만 비교
git diff <커밋 ID> <경로/파일명.확장자>
# 특정 커밋과 특정 파일만 비교 예시
git diff f849edd b.txt
diff --git a/b.txt b/b.txt
new file mode 100644
index 0000000..cf12465
--- /dev/null # 특정 커밋에는 b.txt가 없음
+++ b/b.txt # 워킹 디렉토리에는 b.txt가 있음. 즉, 특정 커밋 이후에 b.txt파일을 추가한 것
@@ -0,0 +1,2 @@
+이름 : b
+나이 : 10
# 브랜치 간의 비교
git diff <브랜치 1> <브랜치 2>
# 브랜치 간의 특정 파일만을 비교
git diff <브랜치 1> <브랜치 2> <경로/파일명.확장자>
git branch
브랜치의 목록과 현재 브랜치를 확인할 수 있으며 브랜치를 생성 및 제거한다.
# 브랜치의 목록과 현재 브랜치를 확인
git branch
# 브랜치를 생성
git branch <브랜치 이름>
# 병합된 브랜치를 제거
# 병합되지 않은 브랜치는 강제로 제거해야 하기 때문에
# -D를 사용
git branch -d <브랜치 이름>
# 현재 브랜치에 merge한 브랜치를 보여줌
# *표시는 현재 브랜치이고, 표시가 없으면
# 이미 현재 브랜치와 merge한 경우이므로
# -d를 사용해 제거거해도 된다
git branch --merged
# 현재 브랜치에 merge하지 않은 브랜치를 보여줌
# -D를 사용해 제거해야 한다
git branch --no-merged
# 브랜치의 이름을 변경 - 특정 브랜치의 이름을 변경하고 싶을 때
git branch -m <변경 전 특정 브랜치 이름> <변경하고 싶은 브랜치 이름>
# 브랜치의 이름을 변경 - 현재 브랜치의 이름을 변경하고 싶을 때
git branch -m <변경하고 싶은 브랜치 이름>
### clone으로 원격저장소를 로컬에 복제한 경우 원격저장소에 있는 브랜치의 목록을 확인
# 원격저장소의 브랜치 목록 확인 - origin은 원격저장소이므로 'origin/브랜치'는
# '원격저장소의 브랜치'임을 나타냄
git branch -r
origin/HEAD -> origin/master
origin/master
origin/signin
# 로컬저장소(.git)와 원격저장소의 브랜치 모두 확인
git branch -a
*master
origin/HEAD -> origin/master
origin/master
origin/siginin
git switch
원하는 브랜치로 이동할 수 있다.
# 브랜치 이동
git switch <브랜치 이름>
git checkout <브랜치 이름>
# 브랜치를 생성하고 바로 이동
git switch -c <브랜치 이름>
git checkout -b <브랜치 이름>
# 이전 브랜치로 이동
git switch -
git checkbox -
# clone으로 로컬에 복제하면 master브랜치만 가져온다
# 그래서 원격저장소의 특정 브랜치를 가져올 때 사용
git switch -t <원격저장소 별칭/브랜치 이름>
git checkout -t <원격저장소 별칭/브랜치 이름>
git restore
수정 내용 또는 스테이징 상태를 되돌릴 수 있다.
# 수정된 내용을 이전 상태로 되돌림
# 단, 언스테이징 상태여야 함
git restore <파일명.확장자>
git restore . # 현재 경로의 모든 파일을 이전 상태로 되돌림
# 스테이징에서 언스테이징으로 되돌림
# 단, 수정된 내용은 유지
git restore --staged <파일명.확장자>
git rm
트랙 상태인 파일(tracked file) 자체를 제거하거나 상태를 제거할 수 있다.(되돌릴 수 있다)
명령어 실행 후, git status명령어를 추가로 실행해보면 delete 표시로 자동으로 스테이징이 된 것을 알 수 있다.
그래서 git rm 실행 후에는 git commit, git push 명령어를 꼭 실행 해야한다.
# 트랙 상태의 파일이 언스테이징일 때, 해당 파일을 삭제
# 워킹 디렉토리와 원격 저장소에서 해당 파일이 삭제 됨
git rm <파일명.확장자>
# 트랙 상태의 파일을 언트랙 상태로 변경할 때 사용
# 워킹 디렉토리에는 해당 파일이 언트랙 상태인채로 존재,
# 원격 저장소에서는 해당 파일이 있다면 삭제 됨
git rm --cached <파일명.확장자>
# 트랙 상태의 파일이 스테이징일 때, 해당 파일을 강제로 삭제할 때 사용(언스테이징일 때도 사용 가능)
# 워킹 디렉토리와 원격 저장소에서 해당 파일이 삭제 됨
git rm -f <파일명.확장자>
# 트랙 상태의 파일이 전부 언스테이징일 때, 파일 전체 삭제
# 워킹 디렉토리와 원격 저장소에서 모든 파일이 삭제 됨
# 단, .gitignore에 해당되는 파일은 제외(-r은 재귀(반복)를 나타내고 .은 현재경로를 나타냄)
git rm -r .
# 트랙 상태의 파일 전부를 삭제
# 워킹 디렉토리와 원격 저장소에서 모든 파일이 삭제 됨
# 단, .gitignore에 해당되는 파일은 제외
git rm -rf .
# 트랙 상태의 파일 전부를 언트랙 상태로 변경
# 워킹 디렉토리에는 모든 파일이 언트랙 상태인채로 존재,
# 원격 저장소에서는 해당 파일이 있다면 삭제 됨
# 단, .gitignore에 해당되는 파일은 제외
git rm -r --cached .
git reset
reset은 기본적으로 '해당 커밋' 상태로 되돌리며 옵션에 따라 해당 커밋 이후의 수정 내용, 스테이징의 상태가 달라진다.
'해당 커밋' 이후의 커밋은 모두 삭제되는데 이것이 싫으면 revert를 사용할 수 있다.
'해당 커밋' 을 지칭하는 코드
- HEAD : 현재 브랜치의 최신 커밋. 기본값이므로 생략 가능
- HEAD^ : 현재 브랜치의 최신 커밋 이전의 커밋(자바스크립트 정규식 표현에서 [^a]은 'a가 아니다' 라는 뜻이므로 HEAD^도 '최신 커밋이 아니다' 가 되므로 최신 커밋 이전의 커밋을 가르킨다고 생각하면 쉽다)
- HEAD~숫자 : 최신 커밋을 제외한 '숫자'만큼의 이전 커밋을 가르킨다.
- 커밋 해시 : 커밋의 ID 값
'해당 커밋'을 지칭하지 않는 코드
- ORIG_HEAD : reset을 하기 전으로 다시 되돌아감
옵션
- --soft : 특정 커밋으로 리셋 및 특정 커밋 이후의 워킹 디렉토리와 스테이징 상태 유지
- --mixed : 특정 커밋으로 리셋 및 특정 커밋 이후의 워킹 디렉토리는 유지, 스테이징 상태는 리셋. 기본값이므로 생략가능
- --hard : 특정 커밋으로 리셋 및 특정 커밋 이후의 워킹 디렉토리와 스테이징 상태도 리셋
# 해당 커밋 이후의 모든 커밋을 제거하여 해당 커밋을 최신 커밋으로 되돌리고
# 해당 커밋 이후의 모든 파일의 스테이징 상태, 수정된 내용은 그대로 유지
git reset --soft <커밋 해시>
# 해당 커밋 이후의 모든 커밋을 제거하여 해당 커밋을 최신 커밋으로 되돌리고
# 해당 커밋 이후의 모든 파일을 언스테이징 상태로 되돌리고
# 해당 커밋 이후의 수정된 내용은 그대로 유지
# --mixed가 reset 명령어의 기본 옵션임
git reset --mixed <커밋 해시>
# 해당 커밋을 한 직후의 시점으로 되돌림
git reset --hard <커밋 해시>
# 최신 커밋을 제거하여 최신 커밋 이전의 커밋을 최신 커밋으로 재설정하고
# 재설정된 최신 커밋의 시점으로 되돌림
git reset --hard HEAD^
# 최신을 커밋을 포함한 n개의 커밋을 제거하여 n개 이전의 커밋을
# 최신 커밋으로 재설정하고 재설정된 최신 커밋의 시점으로 되돌림
git reset --hard HEAD~<n>
# 특정 커밋 시점으로 되돌렸지만 되돌리기 전으로 다시 되돌릴 때 사용
git reset --hard ORIG_HEAD
# 해당 파일을 언스테이징으로 변환, 수정된 내용 유지
# 기본 옵션이 --mixed이기 때문, 특정 파일명으로 되돌리는 것은
# 기본 옵션일 경우에만 가능
git reset <파일명.확장자>
# 스테이징된 모든 파일을 언스테이징으로 변환, 수정된 내용 유지
# 기본 옵션이 --mixed이기 때문
git reset
# 최신 커밋으로 되돌아 갈 때 사용
git reset --hard
git clone
clone은 기본적으로 원격 저장소(github의 레퍼지토리)의 master브랜치만을 로컬에 복제할 수 있다.
특정 브랜치도 복제하고 싶은 경우에는 switch 명령어를 참조할 수 있다.
# 원격 저장소를 로컬에 복제
git clone <원격 저장소의 주소>
git pull
원격 저장소(github의 레퍼지토리)의 해당 브랜치의 최신 버전을 로컬로 가져와 병합한다.
# 원격 저장소의 해당 브랜치의 최신 버전을 로컬로 가져와 병합
git pull <원격 저장소의 별칭> <브랜치 이름>
# 원격 저장소와 로컬 저장소를 rebase
git pull --rebase <원격 저장소의 별칭> <브랜치 이름>
git fetch
원격 저장소(github의 레퍼지토리)의 최신 버전을 가져올 수 있다.
pull 명령어와 달리 병합은 하지 않는다.
# 원격 저장소의 모든 브랜치의 최신 버전을 로컬로 가져옴
git fetch <원격 저장소의 별칭>
# 원격 저장소의 해당 브랜치의 최신 버전을 로컬로 가져옴
git fetch <원격 저장소의 별칭> <브랜치 이름>
# 등록된 모든 원격 저장소의 모든 브랜치의 최신 버전을 로컬로 가져옴
git fetch --all
# 명령에 대한 데모 실행을 수행
# 가져올 때 이 명령이 수행하는 작업의 예시를 출력하되 적용하지는 않음
git fetch --dry-run
### fetch로 원격 저장소의 브랜치를 가져온 후
### checkout 또는 switch 명령어로 해당 브랜치로 이동하면
### --set-upstream옵션이 설정된 상태가 된다
# fetch 안하고 switch 명령어 사용할 경우
# feat/login브랜치가 없으므로 실패
git switch feat/login
fatal: invalid reference: feat/login
# fetch 하고 switch 명령어 사용할 경우
# feat/login브랜치를 만들어 이동하고 --set-upstream 설정 됨
git fetch origin
* [new branch] feat/login -> feat/login
branch 'feat/login' set up to track 'origin/feat/login'.
git switch feat/login
Switched to a new branch 'dev2'
branch 'feat/login' set up to track 'origin/feat/login'.
git merge
병합은 2가지 방법이 있다.
1. 로컬에서 병합 - 로컬에서 base브랜치와 compare브랜치를 병합하여 push하는 방법
2. 원격저장소에서 병합 - 원격저장소에서 Pull Request 요청으로 base브랜치와 compare브랜치를 병합하는 방법
merge 명령어는 로컬에서 병합하는 방법이다.
주의!
- base브랜치 : 병합(merge) 직후에 HEAD가 될 브랜치이므로 병합 하기 전에 base브랜치로 이동한 상태여야 한다.
- compoare브랜치 : base브랜치와 병합 할 브랜치
- base브랜치와 compare브랜치의 타임라인(시간 선)이 같을 때 merge를 하는 경우, base브랜치의 시점이 compare브랜치의 시점보다 뒤져야 한다.(Fast-forward)
- 타임라인이 다른 경우에는 시점은 상관없으나 충돌이 발생하므로 충돌을 해결해야 한다.(Merge Commit 생성됨)
# 기준이 되는 base브랜치(보통은 master)로 이동한 후에
# compare브랜치와 병합
# 즉, base브랜치와 compare브랜치 모두 같은 커밋을 가리킴
git merge <compare브랜치>
# 병합 후에 base브랜치인 master브랜치를 push하면
# compare브랜치는 원격저장소에 push가 되지 않는다
# 'git push origin master'로 master만 push했기 때문이다
# master와 compare브랜치가 병합 되었기 때문에 같은 커밋이므로
# 굳이 compare브랜치를 따로 push하지 않아도 된다
# 또한, 원격저장소에 compare브랜치만 따로 push가 된 후에
# 병합을 해도 된다
# 아래의 순서대로 작업을 하면 된다
git push origin master
git switch -c signin
[...signin에 대해 작업...]
git push origin signin # 여기까지하고 원격저장소에서 Pull Request요청해도 된다
git switch master
git merge signin
git push origin master
# merge 취소
git merge --abort
git rebase
타임라인이 다른 브랜치들을 하나의 타임라인으로 병합하는 방법이다.
분기된 브랜치는 분기된 시점의 커밋을 base로 취급하며, rebase는 말 그대로 base를 재설정 하는 것이다.
위 이미지처럼 master에서 분기된 develop은 분기된 시점인 파란색 커밋을 base로 취급한다.
그리고 develop의 base를 rebase하면 base가 있던 master의 최신 커밋을 base로 재설정하고, develop의 커밋을 순차적으로 이어 붙이면서 하나의 타임라인이 된다.
주의!
- git merge는 병합(merge) 직후에 HEAD가 될 브랜치로 이동한 후에 merge명령어를 실행해야 한다. 그러나 git rebase는 base를 재설정하기 위함이므로 분기 된 브랜치로 이동한 후에 rebase명령어를 실행해야 한다.
- 원격 저장소에 업로드가 되지 않은 커밋들만 rebase해야 한다.
- 충돌이 발생했을 경우💥
- rebase를 계속 진행하기
- 충돌된 파일을 수동으로 수정
- git add / rm <충돌된 파일> 명령어 실행
- git rebase --continue 명령어 실행
- COMMIT_EDITMSG 파일이 생성되면 메세지를 변경 또는 파일 닫기
- rebase 진행 중 특정 커밋 건너뛰기
- git rebase --skip 명령어 실행
- rebase를 하기 전으로 돌아가기
- git rebase --abort 명령어 실행
- rebase를 계속 진행하기
# 타임라인을 하나로 병합
git rebase <base가 있는 브랜치>
~
리눅스 명령어로 루트(root)를 확인한다.
pwd
리눅스 명령어로 현재 위치를 확인한다.
ls
리눅스 명령어로 현재 위치의 폴더, 파일들의 목록을 확인한다.
# 해당 디렉토리 내부의 하위폴더, 파일의 목록을 확인
ls
# 숨김파일과 디렉터리도 함께 표시
ls -a
# 파일이나 디렉터리의 상세 정보를 리스트 형식으로 표시
ls -l
# 파일 정렬 순서를 반대로 표시
ls -r
# 파일 작성 시간 순으로(내림차순) 표시
ls -t
mkdir
리눅스 명령어로 현재 위치에서 폴더를 생성한다.
# 폴더를 생성
mkdir <폴더명>
touch
리눅스 명령어로 현재 위치에서 파일을 생성한다.
# 파일을 생성
touch <파일명.확장자>
code
리눅스 명령어로 폴더, 파일을 에디터(vscode)로 열 수 있다.
# 에디터에서 현재 위치의 해당 파일을 열 수 있음
code <파일명.확장자>
# 현재 위치(pwd 명령어)를 에디터(새창)로 열 수 있음
code .
# 현재 위치(pwd 명령어)를 에디터(현재 창)로 열 수 있음
code . -r
rm
리눅스 명령어로 폴더, 파일을 삭제한다.
커밋한 이력이 있는 파일을 삭제했을 경우에는 git add [파일명.확장자] 또는 git rm [파일명.확장자] 명령어를 통해 스테이징을 하고 삭제했다는 메시지와 함께 커밋을 해야 협업에서 혼동이 생기지 않는다.
# 파일을 삭제
rm <파일명.확장자>
# 모든 파일 삭제
rm *
# 폴더 삭제1
# -r은 재귀라는 뜻으로 폴더와 하위 내용을 삭제
rm -r <폴더명>
# 폴더 삭제2
# -f는 존재하지 않는 파일이나 인수를 무시하고 삭제
rm -rf <폴더명>
# .git을 삭제 하여 모든 커밋을 초기화
rm -rf .git
# 모든 커밋 초기화 후 커밋 히스토리가 남아 있는 동일한 원격 저장소에 새롭게 커밋을 업로드
git init
git add .
git commit -m <'메세지'>
git push -f <원격 저장소의 별칭> <브랜치 이름>
'GIT' 카테고리의 다른 글
git config alias로 명령어 단축 실행 하는 방법 (0) | 2023.11.01 |
---|---|
CLI로 커밋 트리 보는 방법(feat. 소스트리 필요없네!!) (0) | 2023.11.01 |
충돌(conflict)이 일어나는 상황 2가지 (0) | 2023.06.17 |