개인프로젝트 트러블 이슈, 에러 핸들링, 리팩토링

송현섭 ·2024년 7월 21일
0

에러 핸들링

목록 보기
12/13

다사다난한 하루를 보내는 기분이다. 이제 반응형 작업에 들어가서 안그래도 CSS 적으로도 해결할 부분이 많고, 잘 몰라서 검색할 것들이 많은 형편인데 코드적인 이슈는 더 자주 맞아들이는 상황.
지난 한 주간의 짧지만 나름 공부할 가치가 되었던 자잘한 이슈들을 정리해보았다. (개인적인 기록용 작성문이라 설명이 이해안 될 가능성이 높습니다 )



이슈 - 공통 Link 컴포넌트 관련 props 에러


  • Link 태그 사용중 발생한 에러다. 본인은 Link 태그를 emotion의 스타일 컴포넌트로 감싸고 이를 공용으로 만들어 여기저기서 사용하고 있었다. 그 중에 header 의 nav에 쓰이는 각 Link태그가 문제였던 모양.

  • 원인은 생각보다 간단했는데 스크롤 움직임을 감지하여 그 움직임값을 isScrolled 라는 변수로 컴포넌트에 넘겨주고 있는 부분이 문제였다. next.js의 Link 태그는 그 자체 속성으로 href 같은 것은 받지만, 외부의 커스텀형식의 props는 받지 못하는데 필자가 그런 식으로 사용하고 있던 것이었다.




(이런 식의 실수... 사실 이런 구문오류는 은근히 자주 만나게 되는듯. 어려운 부분만 집중하다 보면 사소한 부분을 놓치게 되는 경우가 많은 거 같다.)



  • 사실 이 뒤로 리팩토링 과정에서 애를 많이 먹었다. 일단 공용컴포넌트로 쓰기 위해서 css도 각 사용컴포넌트 성격에 맞게 탈부착되도록 해야했다. 좋은 예로 themeProvider를 이용할 수도 있으나 지금 바로 적용하기엔 무리일 듯 하고, 익숙치 않아서 컴포넌트별로 일단 분리하기로 했다.




  • 결과적으론 이렇게 만들었다. 이 부분에서 타입에러 때문에 굉장히 애를 많이썼다. 일단 Link태그가 props를 못받기 때문에 Wrapper에게 대리로 넘겨주었는데, 얘가 넘겨주는 값들 중 type에 대해 제대로 추론을 못하고 있었다.



  • 분명히 type도 저 rest 안에 포함되어 있는데 왜그런걸까... 구글링도 해보고, chatGpt도 써보고..여기서만 2시간은 붙잡혔다..

  • 여기서 type은 각 Link 컴포넌트 사용하는 것을 구분하기 위한 string값으로, 이 type에 따라 다른 css를 부착하는 식의 구조를 잡았었다. 다만 이 부분을 제대로 설명하고 논의하려면 내부 스타일링 부분을 봐야한다.




  • 내부 style 파일을 보면 현재 이런식으로 되어있다. 파일안에 각 타입별로 css 자체를 정의해두었다. 이후로는 props로 넘겨받은 type값에 따라 필요한 css를 wrapper에 부착만 하면된다.
    (그다지 효율적인 방식은 아닌듯하다. 컴포넌트 별 중복되는 속성도 많고, 사용하는 컴포넌트가 많아질 수록 코드 줄은 배로 늘어날듯...ThemeProvider 공부좀 하고 다음 번에 적용하기로..)



  • 내부에는 getPropsResult 함수도 정의해두었다. 사실 원래는 앞의 emotion css에 필요한 속성을 다 넣어버리고 싶었는데, props를 이용한 조건부 적용에서 타입에러가 났다. 아마 정적인 css에 동적인 props를 사용하려해서 꼬여버린 문제가 아닐까 싶다.

  • 실제로 구글링해보거나 공식문서에서도 css, styled 의 사용용도를 정적이냐 동적이냐에 따라 구분짓고 있다. 문서에서는 동적으로 스타일링에 변화를 주고자 한다면 styled 를 사용하고, 이후 css 와 같이 사용하는 방향을 추천한다.

  • 그런 이유로 위 함수가 만들어졌다. isScrolled 라는 변수에 따라 헤더의 색상을 바꿔야 하는데 이는 동적이기에 css에 넣지 못한다. 그래서 따로 함수로 분리해서 호출 후 return 값을 받는 식으로 적용하고자 했다.
    (추후 동적인 props들이 생길때마다 해당 함수에 다 집어넣고 필요한 것만 뽑아쓰는 식으로 구상중)




  • 여기도 좀 꼬아버린 느낌이 있는데 일단 말하자면 위의 StyledLink 는 말그대로 Link 태그의 default 스타일링 속성이다. 이후 아래쪽의 Wrapper에서 아까 props로 넘겨받은 옵션들을 그대로 sass 구문을 이용해서 Link 태그에 적용시킨다.

  • 아까 언급한 type 부분이 바로 여기에 사용되고 있다. type값에 따라 linkStyles 객체에서 값을 골라와 해당 스타일링을 그대로 부착시키는 건데, 이 부분에서 추론이 안되었던 모양이다.
    추론해보자면 Link 태그는 href 같은 정의된 속성외에 다른 props를 인식하지 못하는데, 필자는 styles.ts 안에서 Wrapper 가 받은 type 이란 props를 그대로 전달하고 있고, 의도되지 않은 값을 갑자기 받아버리게 되니 그로 인해 에러가 발생한 게 아닐까 싶다.






리팩토링 - throttle 함수 구현, 적용 및 리팩토링

  • 프로젝트 진행하면서 구현해 둔 nav 바가 있다. 해당 Bar 는 처음에는 아이템 목차 항목에 숨겨져있다가 토글 버튼을 클릭하면 생겨나는 식의 간단한 구조로 되어있다.
    문제점이 있다면 클릭할 때 마다 빠르게 함수가 재호출되어 기존의 계산과 합산되어 결과가 축적되어 버린다는 것.




  • 본인은 토글 키를 누르면 DOM 객체에 접근하여 해당 요소의 marginBottom 값을 일정범위 더하고, 이후 다시 토글하게 되면 값에서 빼는 식으로 레이아웃의 움직임을 조정하고 있었다.
    그러다 보니 위처럼 빠르게 여러 번 클릭하면 margin 갑이 연달아 합산되거나 빼지게 되어 저런 기이한 결과를 만들어 냈다.






해결책 - throttling (쓰로틀링)을 이용



Throttling

  • 주로 모바일 기기에서 사용되는 용어로, 성능에 무리를 주는 오버클릭으로 인해 디바이스에 무리가 가는 것을 방지하기 위해 성능을 낮추는 것을 의미

  • 프론트엔드의 관점에서 보면 사용자의 무제한적인 요청으로 인해 서버에 부하가 오거나 소프트웨어적으로 손상이 오는 것을 막기위해 이벤트의 발생주기를 조절하는 기술개념

  • 쉽게 말하자면 무언가 트리거 될 때(ex. 클릭이벤트, hover 이벤트) 그에 따른 이벤트 발생 빈도 주기를 간격을 두고 조절하여 연속적으로 이벤트가 발생되지 못하도록 통제하는 것으로 이해할 수 있다.



  • 위 이슈를 해결하기 위해 처음으로 throtling 을 사용해보았다. Debounce는 경험해봐서 익히 알았으나 throtling은 개념만 알고 있었지, 제대로 파고 적용해보는 건 처음이었다.

  • 위 코드는 처음으로 만들었던 코드로, 저 이슈 해결관련 코드는 아니고 다른 곳에서 사용되는 코드다. useRef 에 boolean 상태값을 담아서 사용하고 있다. 조잡해보이지만 실제 throttling 동작원리에 기반하여 작성되었다.

  • 사용자가 처음 이벤트를 발생시키면 해당 함수가 실행되고, isThrottle.current 가 true 로 바뀐다. 이후 아래쪽에 정의된 로직이 실행된다.

  • 이후 반복해서 트리거 시 isThrottle.current 의 값에 따라 분기처리가 되는데, 아래쪽의 setTimeout 이 제대로 처리되기 전까지는 true 상태임으로 이벤트를 발생시키지 않게 된다. 이런 원리로 연속적인 요청에 대한 통제가 되고 있다.




  • 여기는 해당 이슈관련 코드부분이다. 보면 알겠지만 아까 정의한 delaytoTrigger 함수도 아래쪽에서 호출되고 있다. margin 값을 가져오고, 거기에 더하고...관련 로직들이 있는데 일단 지금은 throttling 에 대해서만 보자.

  • 후에 진행한 리팩토링으로 관련코드 캡쳐본이 날아가버렸는데(...'')
    핵심만 보자면 throttling 이 적용은 되었으나 좀 중구난방인 느낌이다. isThrottle.current 도 쓰로틀링 할 함수 내부에서, 중간에서 사용되고 있고, 또 setState로 상태 저장 직후에도 호출해서 사용되고...
    가독성이 떨어질 뿐만 아니라, 독립적인 메서드와는 별개의 코드라서 추후 유틸함수로 뽑아내기도 어려워 보인다.




  • 유틸함수, 즉 순수함수로 분리해냈다. 내부에 callback 인자로 쓰로틀링을 적용할 함수를 넣고 2번째 인자로 delay 시간을 넣어준다. 그러면 쓰로틀링 함수는 args 매개변수를 받아 callback으로 실행하는 익명함수를 다시 반환한다.

  • 이 때 콜백을 받아서 이를 호출하는 함수를 만들어 다시 return 하는 식의 구현방식은 스스로의 코딩에 대해 다시 생각해보게 만들었다.
    본인은 처음에 스스로 구현하는 단계에서 쓰로틀링을 이용하는 함수스코프 내에서 만들어지고 사용되는 변수들은 어떻게 유틸에 전달하지? 매개변수가 많아지겠는데? 같이 1차원적인 구상만 하고 있었다.

  • 그리고 스코프에 대해 다시 복습해보는 시간이 되었다. 함수는 내부에서 선언되었을 때 자기 스코프범위의 변수가 내부 로직에서 사용되고 있다면, 외부에서 호출되어 사용될 때 그 변수를 기억하고 있다는 것!
    그렇기에 전역으로 상태를 만들어 두고 값을 변경하면서 간단한 쓰로틀링 구현이 가능했던 것이다.

  • 콜백을 받아서 이를 호출하는 식을 리턴하면 되는 거였는데...이 고차원적이지만 단순한 구상이 안 떠오른게 정말 한이 되는 순간이었다.
    (기존에 배운 HOC 방식은 되게 잘 써먹는데 이런 부분에서는 막상 안떠오른단 말이지..)




  • 최종적으로 코드는 이렇게 완성되었다. 이 부분은 토글에 따른 margin 값 추가,감소 메서드로 쓰로틀링 부분은 전부 빼버렸다. 지금에서 조건별 분기되는 부분도 다시 분리해도 좋을 것 같다. 다만 너무 세분화시키는 것도 좋은 것은 아니라 일단은 놔두는 중.



  • throttle 실행으로 새로운 익명함수를 반환하기에 이렇게 묶어서 사용하면 된다.

  • 최적화를 생각하면 위 방법보다는 컴포넌트에서 실행해서 새로운 함수 반환하고, 이를 useCallback 같은 방식으로 메모리에 저장해서 누실을 막는 게 더 좋을 거 같단 생각이 든다.



  • 결과는 성공적. 이젠 연타를 해도 안정적으로 일정한 시간간격을 두고 한 번씩만 실행되는 걸 볼 수 있다.






+a) 이슈 - Failed to execute querySelector

  • 리팩토링 과정에서 맞이한 에러다. 토글버튼 클릭 시 발생한 에러로 처음엔 뭔말인지 몰라 해맸다. 관련 이슈 논의도 별로 없는 듯 하고...
    추론하자면 querySelector 를 올바른 형식으로 사용안했다는 거 같은데, 그렇지는 않았다.

  • 필자는 토글 버튼을 클릭하면 해당 버튼의 DOM 요소를 가져온다. 이후 이 요소의 다음 형제 요소를 가져와서 margin 값을 변경해주고 있었다. 처음 클릭 시 event.target 의 id값이 넘어오고 이 id 가 바로 저 navType 변수다.
    아마 문제라면 이 id와 연관있을거라 생각해 추측을 하며 코드를 다시 훑어보는데..



  • 여기서 흐름을 쫒다가 이해가 갔다. querySelector는 보통 id, className 등으로 DOM 요소를 가져올 수 있다. 이 때 만약 id값으로 가져 올 경우 이 id는 고유해야하며, 중복되서 사용되는 일이 거의 없어야 하는 고유성을 지닌다.

  • 그런데 위 코드를 보면 버튼의 id 값과 카테고리 nav의 class 가 똑같다. 사실 이건 한 페이지에서 같은 토글 두 개를 각각 best, new 라는 목차에서 사용하고자 했기에 그것을 구별하기 위해 넣어 준 것이다.
    (id 값으로 버튼 요소정보를 얻고, 이 id의 string값 그대로 다시 같은 클래스의 nav 를 찾아서 스타일링 하는 식으로 구현했었음)

  • 아마 여기서 혼선이 온 것이라 생각된다. 셀렉터가 선택하는 과정에서 클래스만 선택하면 문제가 없으나, 같은 이름의 id도 있어서 여기에 접근을 시도하고, id는 . 선택자가 아닌 # 선택이기에 에러를 던졌을 것으로 추정된다..
    (실제로 이 에러는 무조건적으로 발생한 것이 아니라 수십번의 재클릭 과정에서 한 두번씩 던져지곤 했음)







매번 프로젝트 때마다 에러를 맞이하는 건 어떻게 보면 정말 스트레스 받는 일인 거 같다. 물론 힘겹게 풀어냈을 때의 성취감과 재미는 가치있는 소소한 경험이라고 생각한다.
다만 간단히 해결되는 문제였는데 원인을 못찾아서 몇시간을 헤멜 때마다 너무 많은 시간을 뺏긴 거 같아 답답할 때가 많다.
역시 해낸 경험은 잠깐의 기억에 그칠 뿐 오래가진 못하는 듯 하다. 다음에도 제대로 써먹기 위해 항상 기록하는 것의 중요성을 또 한번 깨닫는다.

profile
막 발걸음을 뗀 신입

0개의 댓글

관련 채용 정보