Woongjae Lee
Daangn - Frontend Core Team ex) NHN Dooray - Frontend Team Leader ex) ProtoPie - Studio Team
Feb 19th, 2026
Timothy @ Daangn Frontend Core
Software Engineer, Frontend @Daangn Frontend Core Team
ex) Senior Lead Software Engineer @NHN Dooray (2021 ~ 2023)
ex) Lead Software Engineer @ProtoPie (2016 ~ 2021)
ex) Microsoft MVP
이 웅재
프로그램이 새 창을 요청하면 데스크톱 환경이 실제로 새 창을 표시합니다.
프로그램이 해당 창에 특정 요소를 그리면 데스크톱 환경이 이를 화면에 표시합니다.
그래픽 툴킷을 사용하여, 간소화
파이썬은 tkinter (Tcl 이라는 언어를 위해 작성된 Tk 라는 라이브러리의 인터페이스)
데스크톱 환경은 클릭이나 키 입력을 프로그램에 알려주고, 프로그램은 이를 처리하고 창을 다시 그립니다.
데스크톱 환경에 창을 만들도록 요청
그 창에 무언가를 그리는데 사용할 객체를 반환 (window)
if __name__ == "__main__":
import tkinter
window = tkinter.Tk()
tkinter.mainloop()pendingEvents 는 데스크톱 환경으로부터 마우스 클릭이나 키 입력 등 쌓여 있는 최근 이벤트를 얻고,
handleEvent 를 이용하여 애플리케이션의 상태를 업데이트 한 뒤에
drawScreen 을 통해 창을 다시 그린다.
# 이벤트 루프 의사 코드
while True
for evt in pendingEvents():
handleEvent(evt)
drawScreen()TkWidget 중에 하나인 Canvas 에 브라우저를 그려요.
if __name__ == "__main__":
import tkinter
WIDTH, HEIGHT = 800, 600
window = tkinter.Tk()
canvas = tkinter.Canvas(window, width=WIDTH, height=HEIGHT)
canvas.pack()
tkinter.mainloop()import tkinter
WIDTH, HEIGHT = 800, 600
class Browser:
def __init__(self, canvas):
self.window = tkinter.Tk()
self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
self.canvas.pack()import tkinter
WIDTH, HEIGHT = 800, 600
class Browser:
def __init__(self, canvas):
self.window = tkinter.Tk()
self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
self.canvas.pack()
def load(self, url):
self.canvas.create_rectangle(10, 20, 400, 300)
self.canvas.create_oval(100, 100, 150, 150)
self.canvas.create_text(200, 150, text="Hello, World!")HTML 문서의 텍스트를 출력 대신 반환
def show(body):
in_tag = False
for c in body:
if c == "<":
in_tag = True
elif c == ">":
in_tag = False
elif not in_tag:
print(c, end="")def lex(body):
text = ""
in_tag = False
for c in body:
if c == "<":
in_tag = True
elif c == ">":
in_tag = False
elif not in_tag:
text += c
return textclass Browser:
def __init__(self):
self.window = tkinter.Tk()
self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
self.canvas.pack()
def load(self, url):
body = url.request()
text = lex(body)
for c in text:
self.canvas.create_text(100, 100, text=c)class Browser:
def __init__(self):
self.window = tkinter.Tk()
self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
self.canvas.pack()
def load(self, url):
HSTEP, VSTEP = 13, 18
cursor_x, cursor_y = HSTEP, VSTEP
body = url.request()
text = lex(body)
for c in text:
self.canvas.create_text(
cursor_x,
cursor_y,
text=c
)
cursor_x += HSTEP def load(self, url):
HSTEP, VSTEP = 13, 18
cursor_x, cursor_y = HSTEP, VSTEP
body = url.request()
text = lex(body)
for c in text:
self.canvas.create_text(
cursor_x,
cursor_y,
text=c
)
cursor_x += HSTEP
if cursor_x > WIDTH - HSTEP:
cursor_y += VSTEP
cursor_x = HSTEP페이지의 모든 요소가 어디에 있어야 하는지를 결정하는 것
페이지 좌표계로 레이아웃을 결정하고, 스크린 좌표계로 페이지를 랜더링한다.
HSTEP, VSTEP = 13, 18
def layout(text):
display_list = []
cursor_x, cursor_y = HSTEP, VSTEP
for c in text:
display_list.append(
(cursor_x, cursor_y, c)
)
cursor_x += HSTEP
if cursor_x > WIDTH - HSTEP:
cursor_y += VSTEP
cursor_x = HSTEP
return display_listWIDTH, HEIGHT = 800, 600
class Browser:
def __init__(self):
self.window = tkinter.Tk()
self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
self.canvas.pack()
def draw(self):
for x, y, c in self.display_list:
self.canvas.create_text(x, y, text=c)
def load(self, url):
body = url.request()
text = lex(body)
self.display_list = layout(text)
self.draw()class Browser:
def __init__(self):
self.window = tkinter.Tk()
self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
self.canvas.pack()
self.scroll = 0class Browser:
def __init__(self):
self.window = tkinter.Tk()
self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
self.canvas.pack()
self.scroll = 0
def draw(self):
for x, y, c in self.display_list:
self.canvas.create_text(x, y - self.scroll, text=c)WIDTH, HEIGHT = 800, 600
SCROLL_STEP = 10
class Browser:
def __init__(self):
self.window = tkinter.Tk()
self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
self.canvas.pack()
self.scroll = 0
self.window.bind("<Down>", self.scrolldown)
def draw(self):
for x, y, c in self.display_list:
self.canvas.create_text(x, y - self.scroll, text=c)
def scrolldown(self, e):
self.scroll += SCROLL_STEP
self.draw()class Browser:
def __init__(self):
self.window = tkinter.Tk()
self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
self.canvas.pack()
self.scroll = 0
self.window.bind("<Down>", self.scrolldown)
def draw(self):
for x, y, c in self.display_list:
self.canvas.create_text(x, y - self.scroll, text=c)
def scrolldown(self, e):
self.scroll += SCROLL_STEP
self.canvas.delete("all")
self.draw()부드럽다고 느껴지도록
frame rate 60 이상
브라우저가 16ms 이내에 다시 그리기를 완료
이러한 이유로 16ms 를 애니메이션 프레임 버짓 이라고 한다.
첫번째: 창의 아래 글자를 그리지 않음
두번째: 창의 위 글자를 그리지 않음
def draw(self):
for x, y, c in self.display_list:
if y > self.scroll + HEIGHT:
continue
if y + VSTEP < self.scroll:
continue
self.canvas.create_text(x, y - self.scroll, text=c)By Woongjae Lee
Daangn - Frontend Core Team ex) NHN Dooray - Frontend Team Leader ex) ProtoPie - Studio Team