[Junior FE] 브라우저는 어떻게 내가 작성한 코드를 실행할까? (1)

신원규·2023년 2월 25일
3
post-thumbnail
(이 시리즈를 모두 읽고 난 뒤 당신의 상상도.jpg)

이제 FE 개발자로 진로를 확정하고 막 공부를 시작하신 취준생분들과, 저와 같이 어쩌다 보니 취업해서 우당탕탕 코딩을 하고있는 신입 FE, 클라이언트 개발자들에게 제가 지난 1년 동안 삽질을 하며 정리한 경험을 나눌까 합니다.

머리말

이벤트 루프? 이벤트 큐? UI 루프? 마이크로 테스크 큐? UI Thread? 다 비슷한 거 아닌가?

여러분은 위 단어들의 차이점을 잘 알고 설명할 수 있으신가요?
저는 취업하고 나서도 한동안 위의 개념을 잘 이해하지 못하고 있었습니다. 뭔가 다른 건 알겠는데, 설명해보라고 하면 입은 잘 안떨어지고...

포스팅 시리즈의 목표는 브라우저(혹은 UI 프레임워크) 의 대략적인 구조를 설명하고 이를 바탕으로 화면을 그릴 때 컴퓨터는 어떠한 동작을 수행하는지,
그렇다면 화면을 잘 그리려면 내가 어떻게 코드를 짜야 하는지 (컴퓨터에게 일을 어떠한 방식으로 수행시켜야 하는지) 에 대해 설명하고자 합니다.

위의 개념들을 모두 이해하고 체화시키면 코드를 작성하거나 볼 때 어째서 이 코드는 좋지 않은 코드인지, 어떻게 개선할 수 있는지에 대하여 스스로 판단하실 수 있을꺼로 생각합니다.

화면을 그린다는 것

화면을 그린다, rendering, paint, 등등의 단어가 의미하는 바가 뭘까요?

React의 경우에는 render() 메소드 안에 JSX를 리턴하는 함수를 작성한다거나, 제가 주로 사용하는 Flutter에서는 Widget의 build method를 정의하면 어떠한 일이 일어나길래, 빌드한 어플리케이션의 화면이 변경되는 걸까요?

먼저, 주사율(Refresh Rate)Frame Rate라는 단어를 이해해야 합니다.
단어의 기원은 브라운관 부터 시작하지만 우리는 지금 디스플레이 공학의 역사를 다루는 게 아니니까, 간단히 정리하자면 주사율의 뜻은 사용하고 있는 디스플레이(ex 맥북의 모니터, 아이폰의 액정)가 1초에 몇 번 새로 쓰일 수 있는지에 대한 단위입니다.
Frame Rate(FPS)하고는 조금 다른 단어입니다. 혼동하지 않도록 주의하세요!

한번 만들어진 디스플레이 하드웨어의 성능이 변할 수는 없으므로 하드웨어 스펙을 의미하는 주사율은 변하지 않는 값이 되는 겁니다. (적어도 하나의 기기에서는요)

그럼 Frame Rate는 어떠한 값이냐, 디스플레이를 통해 화면을 표현하는 우리가 작성한 어플리케이션이 초당 몇 번 새로운 화면을 그리는 명령을 내렸는지 의미합니다.

(이런 상황을 가정해 볼께요)
예시로, 프레젠테이션용의 화면을 생각하면 이 화면은 슬라이드가 넘어갈 때 빼고는 화면의 내용이 바뀌지 않을거라 기대할 수 있습니다. (애니메이션이 없는 슬라이드라 가정해 볼께요).

이때, 화면을 그리고 있는 빔프로젝터(== 디스플레이)는 60Hz의 주사율을 가지고 있지만, 아무것도 변하지 않는 화면을 초당 60번이나 다시 그릴 필요가 있을까요?
슬라이드가 넘어갈 때 빼고는 초당 한번 (1 FPS= Frames Per Second), 혹은 그 이하인 2초에 한 번 (0.5 FPS)여도 충분해 보입니다.

이처럼 Frame Rate는 상황에 따라 값이 유연하게 변할 수 있는 유동적인 값을 의미하게 됩니다. 물론 이 값의 상한선은 사용하고 있는 화면의 최대 주사율인 Refresh Rate와 동일하겠죠.

그래서 이걸 왜 알아야 하는데

왜 내가 이걸 알아야 하지? 라는 생각이 드실 수도 있을 것 같습니다.

그 이유는 바로 Frame Rate를 알아야지만, GUI FrameWork가 맡게 되는 임무를 이해할 수 있게 되기 때문입니다!

GUI 어플리케이션은 그레픽 유저 인터페이스를 사용해 유저의 인풋에 반응하는 이벤트, 데이터 모델의 변경, 서버에서 가져오는 데이터들을 이용해 사용자에게 최대한 빠른 피드백(== 화면의 변화)을 가져다주는 것이 목표입니다.

최대한 빠른 화면의 변화를 제공하기 위해서는 어떠한 동작을 수행해야 할까요?
바로 화면의 주사율과 일치하는 Frame Rate를 어플리케이션이 제공해주면 되겠죠!

디스플레이의 주사율이 60Hz라 가정하면, 60Hz ~= 0.016 초마다 한 번씩 상황에 맞게 다시 그려주면 되는 겁니다.
화면을 그리기 위해서는 드로잉 로직에 앞서서, 뷰 로직, 비즈니스 로직, 데이터 트랜스퍼 로직, 등등의 작업 수행이 필요하겠죠?

이러한 모든 작업이 0.016 초 안에 수행되지 못하면 해당하는 순서의 프레임을 그리지 못하게 되고, 이게 바로 Frame Drop 현상(Latency == 렉 이라 부르기도 합니다)의 원인이 되는 겁니다.

헐 어떡하지

이 사실을 처음 알게 된 순간, 저는 갑자기 큰일이 난 거 같더라구요.
60 FPS를 유지하는 것만 해도 빡빡한데 최신 아이폰들은 주사율이 120Hz까지 지원되니, 120Hz ~= 0.0083초 안에 모든 로직이 다 완료되어야 한다고? 클릭 이벤트 전파하는 시간만 따져도 이거보단 오래걸릴꺼같은데...
앞으로 복잡한 로직들을 어떻게 짜야 하는 걸까 하는 고민이 생겼었습니다.

물론 이 문제를 해결하는 방법이 있습니다!
아마 지금 사용하고 있다는 자각이 없이 사용하고 있을 거고요!
다음 포스팅에서 하나의 프레임을 그리기 위해 UI FrameWork 가 어떤 루틴을 진행하는지에 대해 좀 더 자세히 설명하도록 하겠습니다.
프레임워크가 어떻게 일을 처리하는지 자세히 알수록, 그 목표에 부합하게 코드를 작성할 수 있겠죠?

그럼 저는 다음 포스팅에 돌아오도록 하겠습니다!

profile
생존형 개발자. 어디에 던져져도 살아 남는것이 목표입니다.

0개의 댓글