2023-02-18에 작성된 원문을 수정한 버전입니다
1. 프론트는 항상 맨 먼저 쳐맞는다.
'어 이거 안되는데요? 프론트에서 확인 좀 해주세요'
이런 말을 프로젝트하면서 일주일에 10번도 넘게 들었다. 에러의 원인이 차라리 내(프론트) 문제라면 오히려 낫다. 하지만 백엔드 문제, 혹은 네이티브 문제인 경우도 있다. 하지만 사용자에게 가장 앞단에 보이는 부분은 결국 프론트이다. 일단 누구의 문제이든간에 내가 먼저 버그 issue를 접수받고나서 어느 파트의 문제인지 파악한 다음, 다시 그 파트의 개발자에게 해당 이슈를 토스해야 하는 입장이다.
프론트 개발자 입장에서는 좀 억울하지만 어쩔 수 없는 숙명이다. 사용자는 백엔드가 어떻건, 네이티브가 어떻건은 신경쓰지 않는다. 지금 당장 자신에게 보이는 화면이 에러로 나온다면 일단 프론트 문제처럼 비춰질 수밖에 없다.그래서 나는 이 상황에 대해 '제 잘못이 아닌데요'라고 책임회피를 하려기보다는 그냥 침착하게 '파악해보고 알려드리겠습니다' 라고 대응하는게 더 현명한 방법이라고 결론내렸다.
2. 에러처리(fallback)가 성가시지만 정말 중요하다.
백엔드 API에서 어떤 데이터를 받아온다고 하자.
백엔드에서 보내주는 형식은 res.data.result.list
라는 구조안에 내가 원하는 데이터가 들어있다. 총 4겹으로 감싸져 있는 구조이다. 여기서 중요한 점은, 한 겹을 벗길 때마다 반드시 유효성 검증을 해줘야 한다는 점이다. 하지만 예전의 나같은 경우에는 단순무식하게
// Before
const res = await axios.get("/someApiAddress");
const myList = res.data.result.list;
...
이런 식으로 한 번에 꺼내오려고 했을 것이다. 하지만 위 코드는 서버가 100% 정상 작동할때만 보장되는 코드이다. 만약 서버가 잠시 에러가 났다거나, 아니면 데이터 구조가 바뀌게 되면 위 코드는 여지없이 에러를 내뿜고 말 것이다.
예전의 나라면 그런-상황이-프론트개발자-잘못은-아닌거-같은데요
라고 했겠지만, 지금은 생각이 바뀌었다. 설령 백엔드 단에서 에러가 발생했다 할지라도 그 문제 상황이 프론트(사용자가 보는 화면)단까지 역류해서는 안된다. 최소한 사용자가 보기에 에러가 발생해서 아무것도 없는 흰 화면만 보여주기보다는, '에러가 발생했습니다'라는 텍스트 한 줄이라도 적혀있는 페이지로 리다이렉트 시켜주는게 프론트 개발자가 해야하는 최소한의 예의이다.
가장 훌륭한 방법은 백엔드에서 에러가 발생하여 데이터가 예상과는 다르게 오더라도 이를 잘 핸들링 할 수 있어야 한다. 다시 위의 예시를 들자면
// After
const res = await axios.get("/someApiAddress").catch(fail=>handleFail())
if (res) {
const myList = res?.data?.result?.list
if(myList){
...
} else{
handleFail();
}
}
이런식으로 데이터의 유효성 검증을 반드시 거쳐야 한다. 처음에는 이런 식의 코딩이 비효율적이고 미관상으로도 보기에 좋지 않다고 생각했었는데 ,이건 아무래도 내가 이전까지 코딩을 하던 습관이 주로 Problem solving에 머물러 있었기 때문이라고 생각한다.
PS에서의 코딩을 할 때는 서버랑 통신하지 않고 데이터의 구조를 내가 직접 지정하다보니, 데이터의 유효성을 굳이 검증할 필요를 느끼지 못했다. 하지만 웹개발에서 프론트-백엔드가 맞물리는 작업에서는 반대쪽 진영에서 데이터를 어떻게 보낼 것인지에 대해서는 항상 보수적이고 안정적인 방향으로 코드를 짜야한다.
3. 사용자가 시도할 만한 모든 행동을 테스트하라.
휴대폰 본인인증을 위한 기능을 추가한다고 생각해보자.
사용자가 문자를 받으면 번호를 입력하고 인증 버튼을 눌러주면 참 좋겠지만, 안타깝게도 사용자는 내가 의도한대로만 행동하지 않는다.
첫 번째, 사용자가 전화번호를 잘못 입력했을 수도 있다.
이 상황은 그나마 낫다. 문자를 보내기 전에 서버에서 유효하지 않은 정보라고 에러를 보내주기 때문이다.
두 번째, 인증번호를 잘못 입력할 수도 있다.
이 상황도 충분히 일어날 수 있으며 서버에서도 에러를 띄워주기 때문에 처리하기엔 어렵지 않다.
하지만 세 번째, 사용자가 '인증 요청'버튼을 광클한다.
휴대폰 인증의 경우, 각 요청마다 고유한 인증 ID값이 부여되기 때문에 3번 클릭을 하면 3개의 별개의 인증 ID가 부여되고 3개의 문자가 전송되며 당연히 사용자에게 전송되는 인증번호도 다르다. 그리고 인증과정은 대개 가장 마지막 요청을 기준으로만 유효하기 때문에 사용자는 세 번째 인증번호를 입력해야 하는 상황이다.
여기서 성질 급한 사용자는 세 번째 문자가 오기 전에 첫 번째 인증번호 문자를 입력하고 확인 버튼을 누를 것이다. 인증 결과는 당연히 실패. 당황한 사용자는 세 번째 문자를 기다리는게 아닌, '인증 재요청'버튼을 누를 것이다. 네 번째 문자 요청인 셈이다. 그 순간 사용자에게 두 번째, 혹은 세 번째 인증문자가 도착하고 사용자는 또 다시 인증에 실패한다. 그리고 사용자는 영문도 모르고 이 웹페이지를 만든 개발자(아마 나)를 욕하고 있을 것이다.
누구의 잘못일까? 버튼을 세번 광클한 사용자인가, 그런 행동도 예측 못한 개발자인가. 프로젝트가 진행될수록 느끼는 점은 개발자가 더 신경써야 한다는 것이다. 사용자가 왜 그런 터무니없는 행동을 할까에 대해 원망을 하기 보다, 내가 한발 먼저 터무니없는 행동을 막아야 한다. 이 문제의 경우에는 debounce
함수나 throttle
함수를 이용하여 단 하나의 요청만 전송되도록 처리되도록 해야할 것이다.
이 외에도 시간제한(3분)을 지나서 확인 버튼을 누른다거나 하는 등 내가 의도한 베스트 시나리오에서 벗어나는 행동을 얼마든지 있을 수 있다. 단순 베스트 시나리오만 처리한다면 코드를 단 30줄만 작성하면 된다. 하지만 위에서 언급한 예외적인 사용자의 행동까지 처리하기 위해서는 거의 300줄을 써야 한다. 예전에는 이런 에러 상황 처리로 인해 코드 길이가 길어지는게 현타가 오기도 했는데, 이제는 받아들이기로 했다. 그것이...프론트의 숙명이다.
추후에 나는 이 일을 계기로 TDD,테스트코드의 중요성을 깨닫게 되었다.
4. 안드로이드와 iOS를 동시에 고려해야한다
웹뷰 기반의 하이브리드 앱을 개발하면 프론트 개발자는 반드시 안드로이드(AOS)와 iOS를 동시에 고려해야한다. 대부분의 기능은 하나의 코드로도 양쪽에서 잘 작동하지만, 일부 기능들은 AOS에서만 작동한다거나, 반대로 iOS에서만 작동하는 기능들도 있다.
이런 경우에는 어쩔 수 없이 if문을 이용하여 사용자의 기기를 식별한 다음 분기처리 해주어야 한다. 나는 아이폰을 사용하기 때문에 앱을 만들면서 iOS에서는 정상 작동을 잘 확인하였으나 안드로이드(사실상 갤럭시)에서는 동작이 다르거나 아예 실행되지 않는 케이스도 있었다.
예를 들어 Javascript web share API의 경우, ios에서는 window.navigator.share()
함수만 호출하면 되는 반면에, AOS에서는 별도의 네이티브 함수를 호출해야만 공유하기 기능을 활용할 수 있다. 게다가 javascript의 문제뿐만이 아니라 css에서도 레이아웃이 다르게 보이는 경우가 있다.
(예를 들어 safari에서는 border-radius가 들어간 element에는 outline 속성이 먹지 않는 문제)
그래서 개발할 때는 항상 chrome창과 safari창을 동시에 켜놓고 작업하면서 두 브라우저에서 모두 잘 나타는지 확인해야했다.
5. 데이터 가공은 가급적 백엔드에 맡겨라
이 문제의 원인 역시 PS에서의 습관이 묻어있었기 때문이다.
PS에서는 문제를 풀 때 데이터를 정렬하는 일은 정말 비일비재하므로 웹개발에서 나도 모르게 sort()함수를 하고 있는 나 자신을 발견한다. 하지만 기본적인 시간순 정렬이나, 기간조회 같은 기능들은 프론트가 직접 필터링하기보다는 백엔드 API를 통해서 값을 받아오는게 낫다. 나는 백엔드 통신을 최소화해야만 극한의 최적화를 이룰 수 있다는 강박이 있었는데, 오히려 스크립트가 연산하는 작업이 더 큰 오버헤드 일 수도 있다.
어차피 백엔드는 db에서 값을 꺼내오는 과정이 포함되어있고, 백엔드 자체적으로 가공을 거친 후 보내준다 해도 그렇게 큰 시간 손실은 아니다. 상황에 따라서 데이터 가공을 프론트가 해야할 지 백엔드가 해야할 지를 잘 정해야 한다.
6. 어디까지 나의 '주관적 판단'을 개입시켜야 하는가
내가 프론트 개발을 할 때는 보통 와이어프레임 형태로 되어있는 기획서와 디자이너가 퍼블리싱한 문서를 기반으로 작업한다. 이 둘을 참고해가며 개발하다보면 모호한 부분이 생길 때가 있다. 이를테면 프론트단에서 구현할 수 없는 기능이거나, 왜 필요한 지 알 수 없는 기능이거나 할 때가 있다.
이런 상황에서 어떻게 대처할 지 난처해진다. 잘못된 것 같아도 기획서를 그대로 따라갈 것인지, 아니면 나의 주관을 개입시켜서 기획서와 다르게 개발을 해야할지 말이다. 나는 전자와 후자의 방법 모두를 시행해보았는데 결론적으로 두 번 모두 태클을 받았다.
전자의 방법을 따를 경우,
'기획서가 좀 잘못되더라도 개발하다가 이상하다 느꼈으면 융통성 있게 수정해주세요'
라는 말을 듣고,
후자의 방법을 따를 경우
'그냥 기획서에 나와 있는 그대로만 개발해주세요'
라는 말을 듣는다. 결국에는 정답이 없다. 나의 주관을 정말 '적절하게' 개입시켜야 한다.
7. 개발 자체보다, 사람과의 소통이 더 어렵다
어쩌면 가장 중요한 교훈이다. 개발은 그냥 하면 된다. 모르면 구글링으로 찾아보기도 하고 버그가 발생하면 될 때까지 부딪히다 보면 된다.
하지만 사람과의 소통은 그렇지 않다. 검색을 해도 나오지도 않고 계속 부딪힐 수도 없는 문제이다. 똑같은 의도를 전달하더라도, 내가 사용하는 단어에 따라서 상대방의 대답이 달라지기도 한다.
나는 코드만 잘 짜기만 하면 사람들간의 트러블은 전혀 없을 줄 알았다. 하지만 이번 프로젝트를 통해서 깨닫게 된 점은 결국 프로그램을 만드는 것도 사람, 이용하는 것도 사람이다. 내가 아무리 환상적이게 작동하는 코드를 잘 짜놓았다고 한들 상대방이 납득하지 못한다면 아무 소용없는 일이다.
내가 중점적으로 신경쓴 점은 '나는 어떤 문제상황을 이렇게 받아 들였고, 이런 방식으로 해결하려고 했다.'를 주장하고,
상대방에게 업무를 요청할 때는 '현재 프론트에 이러이러한 문제가 있는데, 이 부분이 원인인 것 같다. 백엔드에서 다른 방식으로 개선해달라' 방식의 의사소통으로 다듬어가고 있다.