What happend?

puppeteer 기반의 크롤러 기능 중 loop를 돌며 화면 내의 '다음' 이동 버튼을 클릭하며 데이터를 수집하는 부분을 개발하고 있었다.

 

클릭 후 자체적으로 setTimeOut을 활용해 만든 delay 함수를 호출시켜 대기 후 루프를 돌게 했는데도 자꾸만 몇개 데이터가 중복으로 들어가서 원인을 찾느라 몇 시간을 썼다.

 

수집할 데이터 - FOTMOB 사이트의 슈팅 상황

 

 

 

What went wrong?

루프를 도는 함수 내에서 click에 await를 걸어주지 않았다.

 

수정 후, 원래는 await가 빠져 있었다.

 

 

 

여기서 주의할 점이, puppeteer에서 제공하는 $를 사용해 가져온 객체는 ElementHandle로 반환되고, querySelector를 사용해 가져온 객체는 그냥 Element로 반환된다는 점이다.

 

다른 로직에서는 eval 함수 내부에서 특정 버튼을 querySelector로 가져왔다. 

그렇게 가져온 요소는 Element이므로 click 시에도 Promise를 반환하지 않으므로 그냥 await 없이 동기 호출했다.

 

이 기억때문에 오류가 난 부분에도 동일하게 await를 걸어주지 않았고, 따라서 click 이후 await 대기한 뒤 후속 작업을 진행해야 하는데 대기 없이 비동기 로직들은 남겨두고 루프가 추가로 돌아버렸고, 그래서 몇번의 중복 루프가 추가적으로 수행 & 중복 데이터가 입력된 것으로 보인다.

 

해당 matchLink는 Element이므로 click 시에도 Promise를 반환하지 않는다.

 

 

 

Lesson & Learned

따라서 puppeteer $ 함수로 가져온 객체를 click시, async 함수 내에서는 반드시 await로 호출해 대기하자.

 

 

 

끝.

 

반응형

What happend?

서비스에 Drag & Drop 기능이 필요하여 react-beautiful-dnd 라이브러리를 사용해 Component를 구현하였다.

DragDropContext와 Droppable을 합쳐 하나로, Draggable을 또 다른 하나로 해서 2개 컴포넌트로 분리했다.

 

리팩토링을 하던 중, 기존에는 발생하지 않던 Warning이 반복적으로 발생하였다.

 

Warning 발생

 

 

 

DragDropUserAttrCard는 사용자가 직접 눌러서 드래그 앤 드롭을 할 컴포넌트(Draggable)인데, 아래와 같이 분리한 해당 컴포넌트 내에는 분명히 key가 존재하는데도 계속 Warning이 발생하였다.

 

key 있다고!

 

 

 

What went wrong?

Key는 분리한 컴포넌트 내부가 아니라, 외부에서 할당해야 오류가 해결되었다.

내가 Customizing한 Draggable인 DragDropUserAttrCard가 사용되는 부분에 key를 선언해야 한다.

 

key는 이 부분에서 할당

 

 

 

또 하나, 나는 이 key가 prop인 줄 알고 그대로 받아서 DragDropUserAttrCard 내부에서 다시 한 번 key를 할당했으나, 그렇게 하면 key는 prop이 아니라는 오류가 떴다.

 

아무리 생각해도 이상해서 DragDropUserAttrCard 선언 시 key를 이상한 값으로 넣어보고 생성된 html을 살펴보니, 해당 key값은 렌더링 시 사용되지 않는 것 같았다.

 

할당해 준 key는 좌측 변환된 html에 없다. 어디에 사용한거지?

 

 

 

Lesson & Learned

  • react-beautiful-dnd 라이브러리 사용 시, Draggable 컴포넌트의 key prop은 크게 의미가 없으며, 렌더링 시 사용되는 Unique한 구분자는 draggable-id이다.
  • 다만 react를 일반 html로 컴파일 시, 해당 라이브러리의 Droppable이나 Draggable과 같은 컴포넌트들이 각각 ul와 li와 같은 html 컴포넌트로 변환되는 과정에서 ul과 li에 필요한 key 속성을 Draggable에 반드시 입력해주어야 하는 것으로 보인다.
  • 이 때, Draggable 영역을 따로 묶어 별도 컴포넌트를 만들었다면, 해당 컴포넌트 소스 코드 내부가 아닌, 컴포넌트를 선언해 사용하는 상위 컴포넌트에서 key를 할당해주어야 한다.

 

 

끝.

반응형

한동안 잘 사용하고 있던 Docker Desktop이 언젠가부터 'Docker Desktop Starting...' 메시지만 무한정 표시되더니 정상적으로 실행되지 않는 현상이 발생하였다.

 

구글링을 통해 여러 해결책을 찾아보았으나, 나의 경우(Windows 10 Home 환경)에는 아래와 같은 순서로 해결했다.

 

 

  1. Docker Desktop 삭제
  2. 재부팅
  3. C:\Users\{본인 사용자 계정}
    위 경로에서 .docker 폴더 삭제
  4. Docker Desktop 재 설치

 

재설치 후 정상 실행된 모습

 

 

 

.docker 폴더를 삭제하지 않고 재 설치시에는 동일한 현상이 여전히 유지되었으므로, 반드시 .docker 폴더를 삭제하고 재설치하도록 하자.

 

 

 

끝.

반응형

typeorm 공식 문서는 조금 불친절한 감이 있는 것 같다.

 

 

What happend?

CreateDateColumn 등의 데코레이터 사용과 관련, 공식 Document에는 별다른 설명 없이 아래와 같은 예제만 덜렁 주어져 있다.

 

typeorm 공식 문서

 

 위 내용을 참고하여 해당 데코레이터만 추가했더니, 테스트 시 created_at 컬럼이 정상적으로 입력되지 않았다.

 

 

 

What went wrong?

검색 결과, 공식 문서에는 별다른 언급이 없지만 DB 내에서 Default Value 설정이 되어 있어야 해당 값을 입력해 주는 방식인 것 같았다.

 

MySQL 테이블 스키마의 디폴트 값을 아래와 같이 CURRENT_TIMESTAMP로 지정해 주어야 행 생성 시 생성 시점이 정상적으로 기록된다.

 

디폴트 값 설정

 

Lesson & Learned

  • typeorm 공식 문서의 불친절함을 배움

 

 

 

끝.

반응형

NestJS 개발 시 DTO로 입력되는 값을 Validation 하고 싶을 때, ValidationPipe를 많이들 사용하곤 한다.

이번에 ValidationPipe를 활용하여 백엔드 API를 개발하던 중 겪은 에러가 있어 기록한다.

 

 

What happend?

우선, NestJS App 생성 시 ValidationPipe의 whitelist & forbidNonWhitelisted 옵션을 모두 true로 설정했다.

    this.defaultApp.useGlobalPipes(
      new ValidationPipe({
        whitelist: true,
        forbidNonWhitelisted: true,
        transform: true,
      }),

 

 

그리고 Postman을 활용하여 POST 로직을 테스트했는데, 아래와 같이 DTO가 상속한 Entity에는 네 항목이 모두 존재하는데도(우측 창 - 해결 후 상황을 재현하느라 Type Decorator들이 주석 처리되어 있다.) 모든 항목이 없어야 한다며 에러를 내뱉는다(좌측 창).

 

에러 발생...

 

 

 

What went wrong?

졸린 와중에 개발 & 검색하느라 원문을 잃어버렸지만, 구글 검색 결과 중 특정 글에서 'DTO의 각 항목은 최소한 하나의 Type Decorator를 가져야 한다'는 글을 보고 아래와 같이 수정하였다.

 

수정 후, 우측 아래 console.log를 보면 정상적으로 body 수신이 되고 있음을 볼 수 있다.

 

 

 

수정 후에는 우측 아래 console.log를 보면 정상적으로 body 수신이 되고 있음을 볼 수 있다.

 

 

 

 Lesson & Learned

  • ValidationPipe whiteList 옵션 사용 시 반드시 Type Check Decorator를 지정하자.
반응형

멍청한 에러들을 기록한다.

 

 

What happend?

NestJS로 백엔드 개발을 하던 중, 기존 Array에 push하는 부분에서 자꾸만 'ERROR [ExceptionsHandler] XXX is not a function' 에러가 발생했다.

 

ERROR [ExceptionsHandler] XXX is not a function

 

 

로직도 수차례 변경해 보고, push 함수가 아니라 새로운 Array를 만들어 slice로 깊은 복사 후 재 할당도 해보고 온갖 짓을 다 했으나 해결이 안 됐다.

 

자고 일어난 다음날, 테스트를 하다가 해결했다.

역시 졸린 상태에서는 개발하는 거 아니다.

 

 

 

What went wrong?

Movie 정보를 담고 있는 this.movie는 Array인데, update시 호출하는 deleteMovie 로직에서 delete 후 Array가 아닌 List로 담는 바람에 오류가 발생했다.

 

List에는 당연히 push function이 없으니까 위와 같은 에러가 발생한다.

 

휴...

 

 

 

 Lesson & Learned

  • API 테스트 결과 확실히 살피기 - 자료형이나 Depth 등이 변하진 않았는지
  • TypeScript 맹신하지 않기 - movies를 Movie[]로 선언하였으나 로직 중에 해당 변수에 List를 할당해도 에러가 발생하지 않는다.
    (그러나 다이렉트로 movies = {}; 따위를 넣어버리면 오류가 발생한다.)

 

 

 

 

끝.

반응형

+ Recent posts