Exec
exec는 현재 셸 프로세스를 새 명령으로 치환(replace)하는 셸 내장 명령이다. exec 없이 명령을 실행하면 셸이 자식 프로세스를 새로 띄우고 자신은 살아남아 그 자식을 기다린다. exec를 붙이면 셸이 새 명령으로 "변신"하며, 같은 PID를 그대로 유지한다.
프로세스 트리 차이
exec 없음
exec 있음
실질적 차이
배포 환경(Render, Docker 등)에서 특히 중요하다.
1. 시그널 전달 (가장 중요)
Render나 Docker가 컨테이너를 종료할 때 PID 1(시작 프로세스)에 SIGTERM을 보낸다.
- exec 없음: SIGTERM이
bash에 전달된다. bash는 기본적으로 이 시그널을 자식 uvicorn에 전달하지 않으므로, uvicorn이 graceful shutdown 기회를 얻지 못하고 타임아웃 후 SIGKILL로 강제 종료된다. 진행 중인 요청이 끊기고 연결이 정리되지 않는다. - exec 있음: uvicorn이 직접 그 프로세스이므로 SIGTERM을 바로 받아 정상 종료한다. 진행 중인 요청을 마무리한 뒤 종료된다.
2. 종료 코드 (exit code)
- exec 없음: bash는 자식이 끝난 뒤 자기 종료 코드를 반환한다. 중간에 bash 로직이 끼면 uvicorn의 실제 종료 코드가 가려질 수 있다.
- exec 있음: uvicorn의 종료 코드가 그대로 노출되어, Render의 헬스/재시작 판단이 정확해진다.
3. 프로세스 1개 절약
불필요하게 대기만 하는 bash 프로세스가 남지 않는다. 사소하지만 깔끔하다.
언제 사용하는가
| 상황 | exec 사용 여부 |
| 셸이 마지막에 단일 서버/명령을 띄우고 더 할 일이 없음 | 사용 (권장) |
| 실행 후 정리 작업, trap 핸들러로 직접 시그널 처리, 여러 프로세스 관리 필요 | 사용하지 않음 |
셸이 마지막에 단일 서버를 띄우고 더 할 일이 없는 경우 exec가 정석이다. 특히 배포 환경에서 graceful shutdown 때문에 권장된다.
반대로 uvicorn 실행 후에도 셸이 무언가 해야 한다면(예: 종료 후 정리 작업, trap 핸들러로 직접 시그널 처리, 여러 프로세스 관리) exec를 빼야 한다.
예시: start-uvicorn
위 스크립트에는 정리 작업이나 trap 핸들러가 없으므로 exec가 적절하다.
참고: 앞단의 read ... < <(...) (host/port 해석)은 exec 앞에 있으므로 영향받지 않는다. 그 부분이 먼저 실행되고, 마지막 uvicorn 실행 시점에만 셸이 치환된다.