읽기 시간 : 10 minutes

당신은 던집니다`./gradlew build`, 터미널이 3분 동안 차단되며, 다른 일을 해야 대기 중 인가요? 새 탭을 열 필요가 없습니다.`Ctrl+Z`프로세스를 일시 중지하고,`fg`그곳으로 정확히 데려다 놓습니다. 이는 개발자의 일상에서 가장 과소평가되는 유닉스 팁입니다.

틱

[]

문제 : 바쁜 터미널

모든 개발자는 이런 상황을 겪었습니다 :

$ ./gradlew build
> Building...

터미널이 잠겼습니다. 더 이상 명령을 입력할 수 없습니다. 파일을 확인하고 실행해야 할`git status`, 또는 점유된 포트를 확인하세요.

@startuml
skinparam backgroundColor #FEFEFE

actor Développeur as dev
participant "터미널" as term
participant "Gradle
(프로세스)" as gradle

dev -> term : ./gradlew build
term -> gradle : Lance le build
gradle -> term : Occupe le terminal\n(pas de prompt)
dev -> term : veut taper une commande
term --> dev : ❌ Terminal bloqué

note right of dev
  Le développeur est
  paralysé pendant
  toute la durée du build
end note
@enduml

대부분의 개발자들은 새로운 터미널을 엽니다. 하지만 더 우아하고, 더 빠르고, 네이티브한 해결책이 있습니다: 작업 관리

세 가지 필수 명령어

Ctrl+Z — 일시 중지 (SIGTSTP)

`Ctrl+Z`신호를 보냅니다`SIGTSTP`포그라운드 프로세스에. 프로세스는중단된— 죽지 않은 — 그리고 터미널이 프롬프트를 되찾는다.

$ ./gradlew build
> Building...
^Z
[1]+  Stoppé    ./gradlew build
$

대괄호 안의 숫자`[1]`(two spaces)잡 번호. Le `+`기본 작업을 나타냅니다.

fg — 앞으로 가져오기

`fg`일시 정지된 작업을 포그라운드로 가져옵니다. 프로세스는 정확히 중지된 지점에서 다시 시작됩니다.

$ fg
./gradlew build
> Building... (reprend où il en était)

특정 작업 번호:

$ fg %2

bg — 백그라운드에서 재개

`bg`프로세스를 sans 블로킹하지 않고 계속 진행하세요. 긴 빌드에 적합합니다.

$ bg
[1]+ ./gradlew build &
$

Le `&`마지막에 그것은 프로세스가 백그라운드에서 실행 중임을 의미합니다.

jobs — 셸의 작업 목록 보기

$ jobs
[1]-  Stoppé    vim README.md
[2]+  En cours   ./gradlew build
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

state "전경\n(차단된 터미널)" as fg #LightCoral
state "일시 중지됨\n(Ctrl+Z)" as stopped #LightYellow
state "배경\n(bg)" as bg_ #LightGreen

[*] --> fg : Lance une commande
fg --> stopped : Ctrl+Z\n(SIGTSTP)
stopped --> fg : fg\nreprend en avant-plan
stopped --> bg_ : bg\nreprend en arrière-plan
bg_ --> fg : fg\nramène en avant-plan
fg --> [*] : Commande terminée

note right of stopped
  Le processus est figé
  mais _pas_ tué
end note

note left of bg_
  Le terminal est libre
  le build continue
end note
@enduml

시나리오 1 : 긴 빌드

이는 가장 일반적인 사용 사례입니다. Gradle 빌드를 실행하면 터미널이 막히고 다른 작업을 해야 합니다.

@startuml
skinparam backgroundColor #FEFEFE
autonumber

actor Développeur as dev
participant "터미널" as term
participant "Gradle" as gradle
database "상태\n프로세스" as state

dev -> term : ./gradlew build
term -> gradle : Lance le build
gradle -> state : En cours (FG)
dev -> term : Ctrl+Z
term -> state : Suspendu (job [1])
term --> dev : Prompt libre $

dev -> term : git status
term --> dev : working tree clean

dev -> term : fg
term -> state : Reprend (FG)
gradle --> dev : Build réussi ✓

note right of state
  Le processus Gradle
  n'a jamais été tué
  Il reprend exactement
  où il s'était arrêté
end note
@enduml

한 걸음씩

$ ./gradlew build
> Building...
^Z
[1]+  Stoppé    ./gradlew build

$ git status
On branch main
nothing to commit, working tree clean

$ fg
./gradlew build
> Building...
BUILD SUCCESSFUL in 2m 34s

시나리오 2 : 잊어버린 편집자

당신은 안에 있습니다`vim`, 다른 파일을 sans 떠나지 않고 확인해야 합니다`vim`.

# Dans vim, en mode commande :
:shell
$ cat /path/to/other/file.txt
$ exit
# Retour dans vim

하지만 그것은 무겁다. 그리고`Ctrl+Z`, 즉시적입니다 :

# Dans vim, n'importe quel moment :
Ctrl+Z
[1]+  Stoppé    vim README.md

$ cat /path/to/other/file.txt
# ... lire le fichier ...

$ fg
vim README.md
# Retour exact là où on en était, curseur en place
@startuml
skinparam backgroundColor #FEFEFE
autonumber

actor Développeur as dev
participant "vim" as vim
participant "껍질" as shell

dev -> vim : édition en cours
dev -> vim : Ctrl+Z
vim -> shell : Suspendu (job [1])
dev -> shell : cat autre_fichier.txt
shell --> dev : contenu du fichier
dev -> shell : fg
shell -> vim : Reprend (job [1])
vim --> dev : Curseur exactement\nà la même position

note right of vim
  vim restaure
  intégralement l'état :
  position du curseur,
  buffer, undo stack
end note
@enduml

참고:`vim`완벽하게 그 상태를 복구한 후`fg`: 커서 위치, 버퍼 내용, 실행 취소 기록 — 전부 보존됩니다.

시나리오 3: opencode 프로세스가 일시 중지됨

이 기사를 쓰게 된 계기는 이 시나리오입니다. 당신은 사용하고 있습니다.opencode(코드에서 LLM을 실행하기 위한 CLI 툴), 그리고 프로세스가 중지됩니다.

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam componentStyle rectangle

actor Développeur as dev
participant "opencode
(LLM 에이전트)" as agent
participant "껍질" as shell
participant "깃" as git

dev -> agent : Session active
agent -> agent : Tour de parole\ndu LLM en cours

note over agent
  Ctrl+Z arrive
  en plein milieu
  d'une analyse de code
end note

dev -> agent : Ctrl+Z
agent -> shell : Suspendu (job [1])
dev -> git : git diff
git --> dev : changements visibles
dev -> shell : fg
shell -> agent : Reprend (job [1])
agent --> dev : Reprend exactement\nl'analyse en cours

note right of agent
  Aucune perte de contexte
  Le LLM reprend sa réponse
  exactement où il en était
end note
@enduml
$ opencode
# ... session en cours, LLM analyse le code ...
^Z
[1]+  Stoppé    opencode

$ git diff HEAD~1
# vérifier les changements récents

$ fg
opencode
# Le LLM reprend là où il s'était arrêté

장점은 두 배입니다:

  1. 컨텍스트 손실 없음: LLM 세션, 대화 기록, 열린 파일 — 모든 것이 보존됩니다

  2. 재시작 없음opencode를 재시작할 필요 없고, 컨텍스트를 재로드할 필요 없고, LLM에게 문제를 다시 설명할 필요 없어

시나리오 4 : 여러 작업을 동시에 관리하기

fg et `bg`여러 개가 일시 중지된 상태에서 특정 프로세스를 대상으로 작업 번호를 받아들입니다.

$ vim config.yml
^Z
[1]+  Stoppé    vim config.yml

$ ./gradlew test
^Z
[2]+  Stoppé    ./gradlew test

$ htop
^Z
[3]+  Stoppé    htop

$ jobs
[1]   Stoppé    vim config.yml
[2]-  Stoppé    ./gradlew test
[3]+  Stoppé    htop

$ fg %2
./gradlew test
# Le build reprend en avant-plan

$ bg %3
[3] htop &
# htop reprend en arrière-plan
@startuml
skinparam backgroundColor #FEFEFE

rectangle "중지된 작업" as jobs #LightYellow {
  card "[1] vim config.yml" as j1
  card "[2] ./gradlew test" as j2
  card "[3] htop" as j3
}

rectangle "행동" as actions #LightGreen {
  card "fg %2
→ 전경" as a1
  card "bg %3\n→ 배경" as a2
  card "fg %1
→ 전경" as a3
}

jobs --> actions : fg/bg + numéro de job
actions --> j2 : Reprend
actions --> j3 : En arrière-plan

note bottom of jobs
  Chaque job conserve
  son état exact
  entre les suspend/resume
end note
@enduml

작업 명령어 요약

주문 효과 예시

Ctrl+Z

전경 프로세스를 일시 중지합니다

전송`SIGTSTP`

fg

기본 작업을 포그라운드에서 재개합니다.

fg ou fg %1

fg %n

작업 n을 전경에서 재개

fg %2

bg

백그라운드에서 기본 작업 재개

bg ou bg %1

bg %n

백그라운드에서 작업 n을 다시 시작

bg %2

jobs

현재 쉘의 작업 목록을 나열

jobs -l(PID와 함께)

kill %n

작업 n 종료

kill %2

fg, bg, Ctrl+Z, Ctrl+C 및 &의 차이

많은 개발자들이 이러한 메커니즘을 혼동합니다. 이것이 결정적인 명확화입니다.

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle "**Ctrl+Z**\nSIGTSTP" as ctrlz #LightYellow {
  card "프로세스를 일시 중지
(메모리에 여전히 존재)
fg/bg와 함께 사용 가능" as c1
}

rectangle "**Ctrl+C**\nSIGINT" as ctrlc #LightCoral {
  card "프로세스를 죽여
(돌아갈 수 없습니다)
상태 전체 손실" as c2
}

rectangle "**& **\n(배경)" as ampersand #LightGreen {
  card "직접 실행
백그라운드에서
터미널을 즉시 자유롭게" as c3
}

rectangle "**fg**
(전경)" as fg_ #SkyBlue {
  card "일시 정지된 작업을 전경으로 가져와
터미널이 사용 중" as c4
}

rectangle "**bg**\n(배경)" as bg_ #Lavender {
  card "작업을 재개합니다\n백그라운드에서 일시 중지된\n터미널 사용 가능" as c5
}

ctrlz -right-> fg_ : fg pour reprendre
ctrlz -right-> bg_ : bg pour continuer\nen arrière-plan
ctrlc -right-> c2 : Pas de retour\nprocessus tué

note bottom of ctrlz
  Le processus est **figé**
  mais **pas tué**
end note

note bottom of ampersand
  Alternative : lancer
  ./gradlew build &
  directement en arrière-plan
end note
@enduml
단축키/명령 신호 효과 뒤로 가시겠어요?

Ctrl+Z

SIGTSTP

프로세스를 일시 중지 (메모리 내에 고정된, 동결된)

네 :`fg` ou bg

Ctrl+C

SIGINT

프로세스를 죽이세요 (완료됨)

아니요 : 파괴된 프로세스

Ctrl+\

SIGQUIT

프로세스를 죽이고 + 코어 덤프

아니요: 프로세스가 파괴되었습니다.

commande &

—

백그라운드에서 바로 실행

`fg`앞으로 가져오기 위해

Ctrl+Z`이다비파괴적. 프로세스는 그대로 메모리에서 정지됩니다. 당신은 즉시 이어서 할 수 있습니다.`fg. 이것은 _pause_일 뿐, _stop_이 아닙니다.

작업의 전체 수명 주기

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

title 셸 작업의 전체 생명 주기
state "존재하지 않는" as none
state "전경
(터미널 점유 중)" as foreground #LightCoral
state "일시 정지됨
(Ctrl+Z / SIGTSTP)" as suspended #LightYellow
state "배경\n(bg ou &)" as background #LightGreen
state "완료
(exit code 0)" as done #LightGray

[*] --> none
none --> foreground : tape une commande
foreground --> suspended : Ctrl+Z
foreground --> done : fin normale

suspended --> foreground : fg
suspended --> background : bg
suspended --> done : kill %n

background --> foreground : fg
background --> done : fin normale\n(en arrière-plan)
background --> suspended : Ctrl+Z\n(si fg puis Ctrl+Z)

done --> [*]

note right of suspended
  L'état suspendu est
  le pivot de tout le
  système de jobs
end note
@enduml

고급 팁

fg와 가장 최근의 직업 및 교대

fg`인수 없이 표시된 작업을 되돌린다.+(가장 최신). 하지만 또한 사용할 수 있습니다%-`이전 작업을 대상으로 지정하려면 :

$ vim file1.txt
^Z
[1]+  Stoppé    vim file1.txt

$ vim file2.txt
^Z
[2]+  Stoppé    vim file2.txt

$ fg      # ramène job [2] (le plus récent)
$ fg %-   # ramène job [1] (le précédent)

작업을 깔끔하게 종료하다

$ jobs
[1]-  Stoppé    vim config.yml
[2]+  Stoppé    ./gradlew test

$ kill %2       # envoie SIGTERM au job 2
$ kill -9 %1    # envoie SIGKILL au job 1 (force)

disown : 쉘에서 분리

disown`쉘의 작업 테이블에서 작업을 제거합니다. 프로세스는 계속 실행되지만 더 이상 불러올 수 없습니다.`fg:

$ ./gradlew build &
[1] 12345

$ disown %1
# Le processus continue mais n'est plus lié au shell
# Ça survives à la fermeture du terminal

nohup과 결합하여 긴 프로세스를 실행

터미널이 종료될 때 프로세스가 살아남게 하려면 :

$ nohup ./gradlew build &
[1] 12345
$ disown %1
# Le build continue même si vous fermez le terminal
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle "fg" as fg_ #SkyBlue {
  card "일을 가져와라\n가장 최근의 (+)" as f1
  card "특정 직업 대상
특정 : fg %n" as f2
  card "교대로 : fg %-" as f3
}

rectangle "kill %n" as kill_ #LightCoral {
  card "SIGTERM (정상)" as k1
  card "SIGKILL -9 (강제)" as k2
}

rectangle "거부하다" as disown #Lavender {
  card "쉘에서 분리
(연결 해제 후에도 살아남음)" as d1
}

rectangle "nohup + &" as nohup_ #LightGreen {
  card "면역된 프로세스
SIGHUP에 대한
(터미널 종료 시 살아남음
터미널)" as n1
}

fg_ -right-> kill_ : si faut\nterminer
kill_ -right-> disown_ : ou détacher
nohup_ -left-> fg_ : fg ramène en\navant-plan

note bottom of disown_
  Après disown, fg ne peut
  plus ramener le job
end note
@enduml

함정과 일반적인 오류

Ctrl+Z와 Ctrl+C를 혼동하다

가장 흔한 실수입니다.`Ctrl+C`프로세스를 죽입니다.`Ctrl+Z`그를 중단합니다. 만약 당신이 누르면`Ctrl+C`반사적으로, 전체 상태가 손실됩니다 — 열린 파일, LLM 세션, 진행 중인 빌드.

@startuml
skinparam backgroundColor #FEFEFE

rectangle "Ctrl+Z (SIGTSTP)" as z #LightGreen {
  card "메모리에서 고정된 프로세스" as z1
  card "fg 그를 무사히 데려온다" as z2
  card "손실 없음" as z3
}

rectangle "Ctrl+C (SIGINT)" as c #LightCoral {
  card "프로세스 완료" as c1
  card "영구적으로 손실된 상태" as c2
  card "돌이킬 수 없습니다" as c3
}

note bottom of z
  Pause → Reprendre
end note

note bottom of c
  Kill → Perdu
end note
@enduml

작업은 셸에 국한됩니다.

jobs, fg et `bg`다른 터미널을 열면 다른 터미널의 작업을 볼 수 없습니다.

# Terminal 1
$ vim file.txt
^Z
[1]+  Stoppé    vim file.txt

# Terminal 2 (nouveau)
$ jobs
# (rien — les jobs sont locaux au shell)

시스템 전체 프로세스를 보려면`ps` ou htop:

ps aux | grep vim

작업은 셸이 닫히면 생존하지 못합니다.

터미널을 닫으면 일시 중지된 작업 또는 백그라운드 작업이 모두 삭제됩니다. 프로세스가 살아남으려면 사용`nohup`+disown:

$ nohup ./gradlew build &
[1] 12345
$ disown %1
# Fermer le terminal → le build continue

대화형 프로세스와 fg

일부 대화형 프로그램은 한 이후에 잘 작동하지 않는다`Ctrl+Z`+fg. 드문하지만 발생한다 :

  • 신호를 사용하는 일부 프로그램(vim, htop, less)은 잘 처리합니다.Ctrl+Z)

  • 장시간 실행되는 네트워크 프로그램(ssh, opencode)은 일반적으로 정상적으로 복구됩니다.

  • 터미널을 수정하는 프로그램(ncurses)은 때때로 터미널을 예기치 않은 상태로 남겨둘 수 있다

터미널이 손상된 후`fg`, 입력 :

reset

요약 다이어그램

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam ArrowColor #444444

title "fg — 모든 것을 바꾸는 팁"

rectangle "문제" as problem #LightCoral {
  card "터미널 차단됨\n프로세스에 의해" as p1
  card "새로운\n터미널 열기 = 무거운 해결책" as p2
}

rectangle "**해결책**" as solution #LightGreen {
  card "Ctrl+Z → 일시 정지" as s1
  card "fg → 다시 이어간다" as s2
  card "배경에서" as s3
  card "jobs → 정지된 것 보기" as s4
}

rectangle "장점" as advantages #SkyBlue {
  card "맥락 손실 없음" as a1
  card "재시작하지 마세요" as a2
  card "터미널 하나면 충분합니다." as a3
}

problem -right-> solution : "네이티브 솔루션
40년 된 유닉스"
solution -right-> advantages : "결과"

note bottom of advantages
  fg n'est pas un tips obscur
  C'est un mécanisme fondamental
  du shell Unix
end note
@enduml

왜 fg가 과소평가되는가?

대부분의 현대 개발자들은 발견합니다`fg`늦거나, 전혀 이루어지지 않을 수도 있습니다. 몇 가지 이유 :

  1. IDE는 터미널을 숨깁니다.: VS Code는 내장 터미널을 제공하지만, 여러 탭이`fg`겉으로는 덜 필요함

  2. 그래픽 도구가 CLI를 대체합니다: 파일 관리자, 시스템 모니터, 그래픽 Git 클라이언트

  3. tmux와 스크린: 이 멀티플렉서는 같은 문제를 다르게 해결하지만`fg`더 간단하고 항상 사용 가능합니다

그런데,`fg`는 :

  1. 보편적인POSIX 셸 모두에 존재 (bash, zsh, fish…​)

  2. 의존 없이tmux 또는 screen을 설치할 필요가 없습니다

  3. 스냅샷: 두 번의 타격 (Ctrl+Z`그리고`fg) 일시 중지 및 재개를 위해

  4. 컨텍스트 보존자: 프로세스의 상태는 무결합니다

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle "왜 fg가
과소 평가됩니까?" as why #LightCoral {
  card "IDE와 터미널
다중 탭" as w1
  card "그래픽 도구\nCLI를 대체하는" as w2
  card "tmux/screen
문제를 해결합니다
다른 방식으로" as w3
}

rectangle "왜 fg 이다\n더 우월한 ?" as superior #LightGreen {
  card "범용: bash,
zsh, fish, sh..." as s1
  card "의존성 없음:
설치 필요 없음" as s2
  card "두 번의 타격:\nCtrl+Z 그리고 fg" as s3
  card "보존된 컨텍스트 :\n상태 손실 없음" as s4
}

why -right-> superior : "간단함
항상 이긴다"
@enduml

기술 메모리 : 신호

호기심을 가진 사람들을 위해, 관련된 신호는 다음과 같습니다:

신호 이름 보낸 사람 효과

2

SIGINT

Ctrl+C

중단 — 프로세스를 종료합니다

3

SIGQUIT

Ctrl+\

코어 덤프와 함께 종료

18

SIGTSTP

Ctrl+Z

대화형 정지 — 프로세스 일시 정지

19

SIGCONT

fg/bg

계속 — 일시 중지된 프로세스를 재개한다

15

SIGTERM

kill %n

깨끗한 종료

9

SIGKILL

kill -9 %n

강제 종료 (차단할 수 없음)

전체 흐름 :

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

participant "사용자" as user
participant "쉘" as shell
participant "프로세스" as proc

user -> shell : Ctrl+Z
shell -> proc : SIGTSTP (signal 18)
note right of proc : Processus figé\nen mémoire

user -> shell : fg
shell -> proc : SIGCONT (signal 19)
note right of proc : Processus reprend\nen avant-plan

user -> shell : bg
shell -> proc : SIGCONT (signal 19)
note right of proc : Processus reprend\nen arrière-plan

user -> shell : Ctrl+C
shell -> proc : SIGINT (signal 2)
note right of proc : Processus terminé\nétat perdu
@enduml

결론

`fg`이것은 obscure한 팁이 아니라, 1970년대부터 사용 가능한 Unix 셸의 기본 메커니즘입니다. 대부분의 개발자들이 이를 모른다는 사실은 우리의 시대를 보여주는 증상입니다: IDE와 멀티플렉서가 우리에게 기본으로부터 멀어지게 만들었습니다.

하지만 SSH 서버에 접속해 있거나, 최소한의 터미널을 사용 중이거나, 단순히 사용 중인 경우`opencode`그리고 LLM은 한창 작업 중입니다 —Ctrl+Z, fg, bg, `jobs`votre 가장 친한 친구들이에요.

_ Ctrl+Z 일시정지, fg 재개. 터미널의 일시정지-재생입니다. _

관련 기사