2시간 동안 안 풀리는 버그가 있었다. 리스트 컴포넌트에서 아이템을 삭제하면 화면에서는 사라지는데, 새로고침하면 다시 나타나는 문제. 사용자가 "삭제했는데 왜 또 있어요?"라고 CS를 넣었다. API 호출은 잘 되고 있었고, 응답도 200이 오고 있었다. 서버에서도 실제로 삭제가 됐다. 네트워크 탭, 콘솔 로그, React DevTools 다 뒤졌는데 원인을 못 찾았다.
콘솔에 찍히는 데이터도 정상이었다. 삭제 후 리스트를 다시 불러오는 API 응답에 삭제한 아이템이 없었다. 근데 화면에는 있다. 대체 왜?
점심시간이 돼서 밖에 나갔다. 회사 앞 산책로를 걸었다. 이어폰도 안 끼고 그냥 걸었다. 가로수 잎이 바람에 흔들리는 걸 보면서 아무 생각 없이. 15분쯤 걸었을 때, 갑자기 생각이 났다. "아, React Query 캐시."
invalidateQueries를 안 하고 있었다. 삭제 mutation은 호출했는데, 리스트를 캐시에서 갱신하지 않아서 새로고침 전까지는 캐시된 옛날 데이터를 보여주고 있었던 거다. 새로고침하면 캐시가 날아가고 서버에서 새로 받으니까 정상적으로 보인 거였고, 하지만 React Query의 staleTime이 지나기 전까지는 캐시를 썼던 거다.
돌아와서 queryClient.invalidateQueries({ queryKey: ['items'] }) 한 줄 추가했다. 끝.
책상에서는 왜 안 보일까
비슷한 경험이 여러 번 있다. 모니터 앞에서 아무리 봐도 안 보이는 게, 자리를 뜨면 보인다. 화장실 갔다 오면서 생각날 때도 있고, 퇴근 후 샤워하다가 떠오를 때도 있다. 한번은 잠들기 직전에 "아!" 하고 벌떡 일어나서 핸드폰 메모에 적은 적도 있다.
내 나름의 가설은 이렇다. 모니터 앞에 있으면 시야가 좁아진다. 에디터에 있는 코드 몇 줄, 콘솔에 찍히는 로그, 네트워크 탭의 요청들. 이 좁은 범위 안에서 답을 찾으려고 한다. 변수 하나하나, 함수 하나하나를 들여다본다. 근데 문제의 원인이 그 범위 밖에 있으면 아무리 봐도 안 보인다. 아까처럼 컴포넌트 코드에는 문제가 없고, 캐시 레이어에 문제가 있는 경우. 코드를 아무리 읽어봐야 답이 없다.
자리를 뜨면 코드에서 물리적으로 벗어나니까, 머릿속에서 전체 그림을 그리게 된다. "이 기능의 데이터 흐름이 어떻게 되지?" 하고 큰 그림을 보면, 코드 레벨에서 놓친 부분이 보인다. 사용자가 삭제 버튼을 누른다 → mutation이 호출된다 → 서버에서 삭제된다 → ... → 화면이 업데이트된다. 이 흐름을 따라가다 보면 "어, 캐시는?" 하는 빈 칸이 보인다. 코드만 보고 있을 때는 "삭제 로직"에만 집중했는데, 걸으면서 "데이터 흐름 전체"를 생각하니까 캐시 레이어가 떠올랐다.
산책 디버깅의 규칙
이제는 일부러 산책을 한다. 디버깅이 30분 넘게 막히면 "산책 갔다 올게요"라고 슬랙에 남기고 나간다. 처음에는 "일하다 산책 가는 게 좀 그런가?" 싶었는데, 시니어가 "좋은 습관"이라고 해줘서 편해졌다. 그 시니어도 막히면 커피를 사러 1층까지 걸어 내려간다고 했다. 엘리베이터 안 타고 계단으로.
나만의 규칙이 몇 가지 있다.
이어폰을 안 낀다. 음악이나 팟캐스트를 들으면 생각이 끊긴다. 청각을 다른 데 쓰면 뇌가 멀티태스킹을 하느라 무의식적 사고가 줄어드는 느낌이다. 그냥 걸으면서 주변 소리를 듣는다. 바람 소리, 차 소리, 새 소리, 공사장 소리. 그 안에서 머릿속이 정리된다.
그리고 해결책을 의도적으로 안 찾으려고 한다. 이게 중요하다. "답을 찾아야 해"라고 생각하면 오히려 안 떠오른다. 압박감이 생기면 사고가 좁아지니까. 그냥 걷는다. 나무를 보고, 하늘을 보고, 지나가는 사람을 보고. 그러다 보면 뜬금없이 코드가 떠오른다. "어, 그 함수에서 null 체크를 안 했는데?" 같은 게 갑자기 스친다. 이 과정이 정확히 어떤 메커니즘인지는 모르겠다. 뇌과학에서 "디폴트 모드 네트워크"라는 게 있다고 들은 것 같은데, 의식적으로 집중하지 않을 때 뇌가 배경에서 정보를 재조합한다는 개념. 자세히는 모른다. 체감적으로는 맞는 것 같다.
보통 15-20분 정도 걷는다. 너무 짧으면 머리가 전환이 안 되고, 너무 길면 아예 다른 생각을 하기 시작한다. 회사 앞 블록을 한 바퀴 돌면 딱 그 정도.
걷기가 좋았던 순간들
디버깅만이 아니다.
한번은 컴포넌트 구조를 어떻게 잡을지 고민이었다. 상품 상세 페이지인데, 탭이 4개 있고 각 탭마다 데이터 소스가 다르다. 상품 정보는 상품 API에서, 리뷰는 리뷰 API에서, Q&A는 또 다른 API에서, 배송 정보는 또 다른 곳에서. 하나의 큰 컴포넌트로 할지, 탭별로 나눌지, 아니면 레이아웃 컴포넌트와 콘텐츠 컴포넌트로 분리할지. 화이트보드에 그려봤는데 어떤 게 맞는지 모르겠었다. 각각의 장단점이 있어서 결정을 못 하겠는 거다.
점심 먹고 15분 걸었다. 걸으면서 "유저 입장에서 이 페이지를 어떻게 쓰지?"를 생각했다. 코드 구조가 아니라 사용자 행동. 탭을 번갈아 누르는 유저가 많을까, 아니면 하나의 탭에 머무는 유저가 많을까. 대부분은 상품 정보만 보고 결제하지 않을까? 그러다 보니 "탭 전환 시 데이터를 매번 불러올 것인가, 처음에 다 불러올 것인가"라는 진짜 질문에 도달했다. 구조 문제가 아니라 데이터 페칭 전략 문제였다. 이것도 자리에 앉아 있었으면 계속 컴포넌트 트리만 그리고 있었을 거다.
또 한번은 팀 회고에서 발표할 내용을 정리하지 못해서 산책을 나갔다. 스프린트 회고라서 "이번 스프린트에서 뭘 배웠는지" 이야기해야 했는데, 기술적인 내용만 떠올리고 있었다. "React Query v5로 마이그레이션했습니다", "검색 자동완성을 구현했습니다." 이런 것들. 10분 걸으면서 "내가 이번 스프린트에서 진짜로 뭘 배웠지?" 하고 생각했더니, 기술적인 것보다 "기획자와 소통하는 법을 배웠다"는 게 떠올랐다. 기획 변경 요청이 있을 때 "알겠습니다" 대신 "이 변경이 일정에 영향을 주는데, 우선순위를 다시 정해도 될까요?"라고 말하는 걸 배운 스프린트였다. 자리에서는 코드 관련 내용만 떠올리고 있었는데, 걸으면서 시야가 넓어진 거다.
팀에 퍼뜨리기
최근에 팀 내에서 "산책 디버깅"이라는 말이 생겼다. 내가 자주 나가는 걸 보고 다른 동료도 따라했는데, 효과를 봤다고 한다. CSS 레이아웃 문제를 산책하고 와서 풀었다는 동료가 있었다. position: sticky가 안 먹는 문제였는데, 걸으면서 "부모 요소에 overflow: hidden이 있나?" 하는 생각이 떠올랐다고. 돌아와서 확인하니까 맞았다고.
이제는 누가 막히면 "산책 갔다 와"가 자연스러운 조언이 됐다. 팀 슬랙에 "산책 디버깅 중"이라는 상태 이모지도 생겼다.
물론 모든 문제가 산책으로 풀리는 건 아니다. 타입 에러 같은 건 산책을 아무리 해도 안 풀린다. Type 'string' is not assignable to type 'number' 같은 건 IDE에서 빨간 줄 보면서 해결해야 하는 문제다. 산책이 도움이 되는 건 "큰 그림"이 필요한 문제다. 아키텍처, 데이터 흐름, 설계 판단, 디버깅에서 원인을 못 찾을 때. 코드의 숲이 안 보일 때 산책이 도움이 된다.
가끔은 답이 안 떠오를 때도 있다. 30분 걷고 돌아와서도 여전히 모르겠으면? 그냥 다시 코드를 본다. 적어도 머리가 좀 환기됐으니까, 같은 코드를 봐도 조금 다르게 보인다. 그리고 못 풀면 시니어한테 물어본다. 산책이 만능은 아니니까.
걷기는 비용이 0이다. 도구도 필요 없고, 구독도 필요 없고, 신발만 있으면 된다. 15분 투자해서 2시간 삽질을 아낄 수 있다면 꽤 괜찮은 거래다.
요즘은 산책을 디버깅 용도가 아니어도 한다. 점심 먹고 그냥 나간다. 특별한 목적 없이. 걸으면서 하늘을 보고, 빌딩 사이로 바람이 지나가는 걸 느끼고. 하루 종일 모니터만 보다가 밖에 나오면 눈이 편해지는 게 느껴진다. 초점 거리가 바뀌니까. 가까운 화면만 보다가 먼 산을 보면 눈 근육이 이완되는 느낌. 이것만으로도 산책할 이유는 충분하다.
