![]()
Python REPL에서 창 크기 바꾸면 화면이 깨질 때: CPython의 ‘리사이즈 이벤트’ 처리 방식이 바뀐 이유
meta_description: CPython PR #146459(gh-146458)는 Windows에서 콘솔 창 리사이즈 시 REPL의 높이/너비 추적이 틀어지는 문제를 수정했다. 핵심은 SIGWINCH 핸들러에서 즉시 크기를 갱신하던 방식을 바꾸고, refresh 단계에서 getheightwidth()로 화면 크기를 다시 계산하도록 만든 것이다. 이 글은 왜 이벤트 핸들러에서 상태를 미리 바꾸면 문제가 생기는지, pyrepl 기반 REPL에서 “resize” 이벤트가 어떻게 흘러가는지, 그리고 터미널 UI/CLI 도구를 만드는 개발자가 적용할 수 있는 실전 체크리스트를 정리한다. meta_keywords: python,repl,pyrepl,console,windows,resize,sigwinch,event queue,terminal ui,tty,refresh,rendering,bugfix,regression,test,mock,height,width meta_robots: index,follow
REPL이나 터미널 기반 CLI를 만들다 보면, 은근히 사람을 미치게 하는 버그가 있다.
- 창 크기 바꾸면 화면이 깨짐
- 커서 위치가 튐
- 줄바꿈이 꼬여서 입력이 보이지 않음
특히 Windows 콘솔은 환경이 다양해서(기본 콘솔/Windows Terminal 등),
- “내 컴퓨터에서는 되는데”
가 자주 나온다.
최근 CPython에 들어간 PR #146459(gh-146458)는 딱 그 지점을 고쳤다.
- 콘솔 리사이즈에서 REPL의 height/width 추적이 틀어지는 문제
이번 글은 단순 ‘버그 픽스’ 소개가 아니라,
- 왜 이런 버그가 생기는지
- 터미널 UI를 만드는 입장에서 어떤 패턴이 안전한지
를 실무적으로 정리한다.
![]()
1) PR #146459가 바꾼 핵심: “리사이즈 이벤트”와 “크기 갱신”을 분리
diff를 보면 핵심 변경은 2줄로 요약된다.
1) 리사이즈 신호/이벤트가 왔을 때는
- 이벤트 큐에 resize 이벤트만 넣고
- height/width를 그 자리에서 바꾸지 않는다
2) 실제 화면을 다시 그릴 때(refresh)
- getheightwidth()로 현재 콘솔 크기를 다시 읽고
- 그 값으로 렌더링을 계산한다
즉,
- 이벤트 핸들러(비동기)에선 상태를 “예약”만 하고
- 렌더링 단계(동기)에선 상태를 “확정”한다
이 구조가 된 것이다.
2) 왜 이벤트 핸들러에서 height/width를 미리 바꾸면 위험할까
터미널 UI는 보통 이런 3단계를 가진다.
- 입력/이벤트 수집
- 상태 업데이트
- 렌더링
문제는 리사이즈가 이 중간 어디서든 끼어들 수 있다는 점이다.
리사이즈 핸들러에서 height/width를 즉시 바꿔버리면,
- 아직 이전 프레임(screen)이 남아있는 상태에서
- 새 크기 기준으로 커서/오프셋을 계산하게 되고
- 결과적으로 화면이 “부분적으로” 꼬일 수 있다
이게 “가끔만 터지는” 이유다.
- 타이밍 이슈
그래서 실무에서는 보통 이런 규칙이 안전하다.
- 비동기 이벤트에서는 ‘플래그/이벤트’만 쌓고
- 실제 계산은 렌더링 루프에서 한 번에
PR #146459는 CPython REPL에도 그 원칙을 적용한 셈이다.
3) pyrepl의 resize 이벤트 흐름을 운영자 시선으로 보면
pyrepl 쪽 구조를 아주 단순화하면:
- 콘솔에서 이벤트를 읽어서 event_queue에 넣는다
- reader가 event를 처리하면서 screen을 계산한다
- console.refresh가 실제 출력/커서 이동을 한다
여기서 리사이즈는 “입력 이벤트” 중 하나다.
이번 변경으로,
- 리사이즈 이벤트가 들어오면
- refresh가 호출되는 순간에
- 콘솔 크기를 다시 읽는다
즉, 프레임 경계에서만 크기가 갱신된다.
이건 터미널 UI에서 흔히 쓰는 패턴이다.
- 프레임 시작 시: 입력 스냅샷
- 프레임 렌더 시: 크기 스냅샷
4) 터미널 UI/CLI 개발자가 바로 적용할 수 있는 체크리스트 7개
이 PR을 읽고, 내 코드에 바로 적용할 수 있는 체크리스트를 정리한다.
1) 리사이즈 핸들러에서 “렌더링 상태”를 직접 건드리지 않기
2) 리사이즈는 이벤트 큐에 넣고, 루프에서 처리하기
3) refresh 직전에 현재 크기를 다시 읽기(가능하면)
4) 화면 계산(calc_screen)과 출력(refresh)을 분리하기
5) 커서 위치(cxy)처럼 깨지기 쉬운 값은 “calc_screen이 만든다”를 원칙으로
6) 테스트에서 height/width는 하드코딩보다 getheightwidth()를 mock으로 주입하기
7) Windows/Unix 둘 다에서 리사이즈 케이스를 최소 2개(넓어짐/좁아짐) 고정 테스트로
PR diff에서도 테스트가 getheightwidth를 side_effect로 바꿔, 리사이즈 상태를 더 명확히 시뮬레이션하도록 정리된 게 보인다.
5) 실전 디버깅: ‘화면 깨짐’ 리포트가 왔을 때 바로 물어볼 6가지
터미널 UI 이슈는 재현이 어려워서, 질문을 잘해야 시간이 줄어든다.
내가 바로 물어보는 건 아래 6개다.
1) 어떤 OS/터미널인가? - Windows Terminal / 기본 콘솔 / VSCode 내장 터미널 / WSL 등
2) 폰트/배율/HiDPI 설정이 있나? - 배율이 들어가면 리사이즈 이벤트가 더 자주(혹은 이상하게) 들어오기도 한다.
3) 창을 “드래그로 늘리는 중”에도 깨지나, “늘린 뒤”에만 깨지나? - 전자는 프레임 경계/이벤트 폭주 이슈일 가능성이 크다.
4) 멀티라인 입력에서만 깨지나? - pyrepl 테스트도 멀티라인 함수 입력 케이스를 따로 둔다.
5) 깨진 뒤에 ‘Enter’를 한 번 치면 복구되나? - 복구되면 refresh 타이밍이 힌트다.
6) 화면이 깨질 때 커서 좌표가 어긋나는가, 내용이 잘리는가? - 좌표 문제면 cxy/offset 계열 - 잘림 문제면 height/width 스냅샷 문제
이 질문 6개만으로도 “어디를 봐야 하는지”가 크게 좁혀진다.
그리고 재현이 되기 시작하면, 그 다음은 ‘최소 재현 스크립트’를 만드는 게 가장 빠르다.
예:
- 멀티라인 입력을 일부러 만들기
- 리사이즈를 넓게/좁게 각각 한 번씩 만들기
- 마지막에 화면 스냅샷(출력)만 남기기
터미널 UI 버그는 복잡한 서비스 코드 전체에서 찾는 것보다, 입력 시퀀스를 고정해두는 게 효율이 훨씬 좋다.
6) 실전 적용: 내 CLI/REPL에 ‘resize 안정성’을 넣는 가장 작은 변경
이 PR에서 가져올 수 있는 가장 작은 변화는 이거다.
- 렌더링 직전에 “현재 화면 크기”를 다시 읽는다
예를 들어 구조가 이런 식이라면:
# pseudo-code
while True:
ev = get_event()
handle_event(ev)
screen, cursor = calc_screen()
refresh(screen, cursor)
여기서 refresh 직전에만 크기를 갱신해도, 리사이즈로 인한 화면 깨짐이 크게 줄어든다.
# pseudo-code
while True:
ev = get_event()
handle_event(ev)
height, width = getheightwidth()
screen, cursor = calc_screen(height, width)
refresh(screen, cursor)
물론 실제 코드는 더 복잡하지만, 요지는 동일하다.
- 계산에 쓰는 크기 스냅샷을 프레임 경계로 옮겨라
이 원칙만 잡아도, “가끔 깨짐” 같은 타이밍 버그의 대부분이 사라진다.
6) 결론: 리사이즈 버그는 “계산 시점”을 고치면 대부분 해결된다
창 크기 바뀔 때 REPL이 깨지는 버그는,
- 출력 코드가 나쁘다
기보다는,
- 상태를 언제 갱신하느냐
에서 많이 터진다.
PR #146459는 그걸 “이벤트에서는 예약, refresh에서 확정”으로 바꿨다.
터미널 UI를 만드는 입장에선,
- 리사이즈를 ‘특수 케이스’로 때우지 말고
- 프레임 경계로 흡수하라
는 교훈을 준다.
![]()
Sources
- CPython PR #146459 — gh-146458: Fix repl height and width tracking on resize
- https://github.com/python/cpython/pull/146459
Keywords
python,repl,pyrepl,console,windows,resize,sigwinch,event queue,terminal ui,tty,refresh,rendering,bugfix,regression,test,mock,height,width
이미지 크레딧/라이선스
- Looking at computer and phone (Unsplash).jpg — Tim Gouw / CC0
- https://commons.wikimedia.org/wiki/File:Looking_at_computer_and_phone_(Unsplash).jpg
- http://creativecommons.org/publicdomain/zero/1.0/deed.en
- Laptop coding programs (Unsplash).jpg — Christopher Gower / CC0
- https://commons.wikimedia.org/wiki/File:Laptop_coding_programs_(Unsplash).jpg
- http://creativecommons.org/publicdomain/zero/1.0/deed.en
댓글
댓글 쓰기