일이 지났습니다. 시의성에 유의하세요
서문
프로젝트 경험이 부족해서 프론트엔드와 백엔드 간의 역할 구분에서 자주 실수를 저지릅니다.
본문
예를 들어, 버튼 클릭 시 백엔드에서 제공한 API로 데이터를 조회한 후 Handlebars를 통해 프론트엔드에 표시하는 기능을 구현해야 했습니다. 하지만 매번 조회되는 데이터(반환 배열, 각 항목은 map 타입)가 상당히 길어서 한 번에 모두 로드해 페이지에 표시하는 것은 적절하지 않았습니다. 그래서 저는 "버튼을 클릭할 때마다 백엔드에서 데이터를 조회"하도록 설계한 후, 매번 얻은 데이터를 slice 처리해 동일한 길이로 잘랐습니다. 즉 첫 번째는 0부터 x까지, 두 번째는 x부터 2x까지, 이런 식으로 처리했습니다.
하지만 여기에는 문제가 있었습니다. 백엔드의 SQL 쿼리 결과가 order by로 정렬되었는지 확실하지 않았기 때문입니다. 만약 정렬되지 않았다면, 버튼을 클릭할 때마다 조회되는 데이터는 무작위 순서가 될 것입니다. 배열의 순서를 보장할 수 없다면, 순차적으로 잘라낸 데이터가 원하는 대로 나오리라는 보장도 없습니다. 예를 들어 이번에 잘라낸 0부터 x 항목에 h 항목이 포함되어 있다면, 다음 조회 시 이 h 항목이 x+h 위치로 이동해 다시 조회되어 Handlebars에 전달될 수 있습니다. 이는 잘못된 방식입니다.
당연히 제가 할 수 있는 범위 내에서 이 문제를 해결하려 했습니다. 그래서 프론트엔드에서 배열 항목의 uid 같은 속성으로 정렬(sort)하려고 했습니다. 그런데 프론트엔드 리더가 제가 무엇을 하는지 묻더니, 백엔드에서 조회된 이 정렬되지 않은 배열을 정렬하려 한다고 말하자 이렇게 말했습니다:
『이건 백엔드에서 처리해야 할 일이야. 프론트엔드에서 하면 성능에 영향을 줄 거야』
그리고 설명을 이어갔습니다:
『일반적으로 프론트엔드는 백엔드에서 제공한 API를 그대로 사용해야 하며, API로 조회된 데이터를 다시 가공할 필요가 없어. 하지만 Handlebars 같은 템플릿 안에서는 가끔 조회된 데이터의 속성을 조합해 처리한 후 페이지에 출력해야 할 때도 있지. 근데 네 경우에는 분명히 백엔드에서 처리해야 할 일이야. 그들이 API를 제공할 때 요구사항을 충족시켜야 한다고』
『지금 네 상황은 페이징 기능을 구현해야 하는데, 백엔드 API가 매번 전체 내용을 조회할 수만 있고 필요한 만큼만 조회할 수는 없는 거야. 즉, 기존 API로는 요구사항을 충족시킬 수 없어. 어떻게 해야 할까? 두 가지 방법이 있어: 시간이 촉박하다면 프론트엔드에서 성능을 희생하고 바로 처리해버리는 거야. 시간이 넉넉하다면 백엔드에 API를 요청하는 거지. start와 offset을 추가하는 정도야. 이 API는 어렵지 않게 작성할 수 있어.’
또 다른 최적의 실천 방법은 조회 연산은 최대한 적게, 페이지에서 정보를 얻을 수 있다면 데이터베이스에서 정보를 가져오지 않는 것입니다.
예를 들어 백엔드에 API를 추가한 후에도 여전히 네 번의 조회 작업이 필요합니다: 초기화, ‘더보기’ 클릭, 탭 전환. 매번 성능 손실이 발생하는데요, 왜 그럴까요? 제 논리는 이렇습니다:
첫 번째: 페이지 초기화 시 데이터를 조회하고 고정 길이의 배열을 잘라 페이지에 데이터를 표시합니다(조회 1회). ‘더보기’ 버튼을 클릭할 때마다 횟수를 파악해 최대 클릭 횟수를 판단해야 합니다. 조회된 전체 데이터 길이를 매번 필요한 데이터 길이로 나누고 정수 부분 +1을 하면 필요한 클릭 횟수를 얻을 수 있습니다. 이만큼 클릭해야 모든 데이터가 로드되고 ‘더 이상 없음’ 버튼이 표시됩니다(조회 1회). 이건 당연한 거죠?
두 번째: ‘더보기’ 클릭 시 데이터를 조회해 필요한 길이만큼 페이지에 표시한 후 클릭 횟수 count+1(조회 1회)
세 번째: '내 커뮤니티’와 ‘내 활동’ 두 개의 탭이 있어서 이 버튼들을 클릭할 때마다 조회가 필요합니다(조회 1회)
가장 충격적인 것은 제가 '내 커뮤니티’와 ‘내 활동’ 탭의 내용을 겹쳐놓았다는 사실입니다! 즉 버튼을 클릭할 때마다 기존 내용이 jQuery의 $(container).html() 메서드로 강제 교체됩니다. 단일 컨테이너만 사용한 제 설계 때문에 탭 전환 시에는 html을 사용할 수밖에 없고, ‘더보기’ 클릭 시에만 append를 사용할 수 있었습니다!
정말 부끄러운 일입니다.
후기
이후 저는 앞서 언급한 최적의 실천 방법을 따랐습니다. 또한 가능한 한 기능을 분리하고 집중시키려 했습니다. 이게 무슨 뜻일까요? 즉, 탭 전환은 탭 전환만 담당하고 데이터 조회는 하지 않습니다(기능 분리). ‘더보기’ 버튼은 데이터 조회만 담당하며 다른 일은 하지 않습니다. 심지어 페이지 초기화 시에도 trigger로 이 버튼을 활성화해 데이터를 조회합니다. 페이지 로드 초기에 먼저 조회하고 버튼 클릭 시 다시 조회하는 방식이 아닙니다(기능 집중).
저는 인생의 중요한 선택의 기로에 섰을 때, 누군가 최선의 방법을 알려주어 소중한 시간을 헛되이 보내지 않기를 바라곤 합니다. 그런 마음으로 저는 자주 블로그를 쓰며, 광활한 인터넷의 이 작은 구석에 제게는 단 한 번뿐인 인생 경험을 기록하여 도움이 필요한 분들에게 도움이 되기를 바랍니다.