Skip to content

Conversation

@Seoin02
Copy link
Collaborator

@Seoin02 Seoin02 commented Dec 1, 2024

공부한 것

  • Web Component
    • 그 기능을 나머지 코드로부터 캡슐화해 재사용 가능한 커스텀 엘리먼트를 생성하고 웹 앱에서 활용할 수 있게 하는 다양한 기술의 모음
    • 프레임워크에 종속되지 않고 독립적인 UI 구성 요소를 만들 수 있습니다.
  • state, setState, render를 class형 컴포넌트, 타입스크립트로 구현하면서 상태 변경 시 this 바인딩을 해줘야해 번거로웠습니다.(물론 객체지향에서 this를 사용해야 하는 게 당연하지만) 이때 리액트의 선언적인 상태 관리가 정말 편하다는 것을 깨달았습니다.
  • 이벤트 발생 시 스프레드 문법을 사용해 기존 배열을 복사하는 방식으로 상태를 관리합니다. 이 이유는 리액트는 상태가 변경되면 해당 컴포넌트를 리렌더링하는데, 하나의 객체의 상태를 직접 수정하면 리액트가 상태 변경을 감지하지 못하기 때문입니다.
  • 리액트의 재조정 알고리즘은 Diffing 과정을 거쳐 가상 DOM 트리 2개를 비교합니다. 이때 리액트는 각 요소의 고유한 key를 통해 어떤 요소가 변경됐는지 식별합니다. key를 지정하지 않으면 리액트는 배열의 모든 요소를 비교해야 하므로 성능이 저하되고, 요소가 잘못 매핑될 수 있습니다.
  • main.ts와 App.ts를 따로 분리하는 이유는 진입점은 진입점 역할만 하고, 기능적인 루트 파일 역할은 App.ts가 하면서 역할 분리를 하기 위해서 입니다.
  • 클래스형 컴포넌트에 타입을 적용할 때 props(컴포넌트 외부에서 전달되는 속성)와 state(컴포넌트 내부에서 관리되는 속성)의 타입을 더 직관적으로 확장성있게 사용할 수 있습니다. class Component<Props = TComponentData, State = TComponentData>
  • keyof: 올바르지 않은 이벤트 타입이나 태그 이름을 사용하면 컴파일 오류를 발생시켜 개발자의 실수를 줄일 수 있습니다.

고민점

  • 왜 class로 구현했을까 이유가 궁금했는데, class엔 상태와 생명주기를 관리할 수 있는 메서드를 사용할 수 있어서 더 명확하게 구현할 수 있겠구나 하고 납득했습니다. 확실히 사용해보니 메서드가 직관적이고, 메서드로 생명 주기를 커스텀 할 수 있는 게 장점이었습니다.
  • 이벤트 버블링을 적용할 때 'event를 각각의 하위 요소가 아니라 component의 target 자체에 등록하는 방식으로 생명주기를 바꿔야 한다'는 설명이 잘 이해가 되지 않았습니다. 기존에 하위요소에 이벤트를 등록하면 요소가 동적으로 추가될 시 추가된 요소에 다시 이벤트 리스너를 등록해야 하는데, 하위요소가 아닌 상위요소에 component가 생성되는 시점에만 이벤트를 등록하면 된다는 내용으로 이해했습니다.
  • 아래 코드를 사용할 때 dataset 속성을 태그마다 달아줘야 하는 게 비효율적이라고 생각해서 이벤트 위임 방식도 고려했지만, 이벤트 위임 방식보다 가독성이 좋을 것 같아 dataset 속성을 사용했습니다.
return `
      <ul>
        ${filteredItems.map(({ id, content, active }: IItem) =>
        `<li data-id="${id}">
            ${content}
            <button class="toggleButton" data-id="${id}" style="color: ${active ? '#09F' : '#F09'}">
              ${active ? '활성' : '비활성' }
            </button>
            <button class="deleteButton" data-id="${id}">삭제</button>
          </li>`).join('')}
      </ul>
    `
  • mounted()가 리렌더링마다 반복되는 게 불필요하다고 생각해서 조건문을 달아 초기 렌더링 시에만 mounted()를 실행하게 수정했습니다.

@Seoin02 Seoin02 added the ✨ Feat Feature label Dec 1, 2024
@Seoin02 Seoin02 requested a review from D5ng December 1, 2024 16:55
@Seoin02 Seoin02 self-assigned this Dec 1, 2024
@Seoin02 Seoin02 changed the base branch from main to seoin_dev December 2, 2024 14:36
src/App.ts Outdated

get filteredItems(): IItem[] {
const { isFilter, items } = this.state;
return items.filter(({ active }: { active: boolean }) => (isFilter === 1 && active) || (isFilter === 2 && !active) || (isFilter === 0) )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

items가 타입이 지정되어있으면 매개변수에 타입을 지정할 필요가 없어요. state에 타입을 지정해주는게 좋아보입니다!

제네릭도 사용할만하겠네요

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

state에 Record<string, any>를 지정하면 초기값을 빈 객체로 지정할 때 에러가 떠 매개변수 타입을 IItem으로 지정했습니다

Copy link
Contributor

@D5ng D5ng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제 주관적인 의견이에요. 피드백은 솔직하게 달도록 하겠습니다!

[구현]

구현은 황준일님 아티클과 비슷하게 했지만, 여기에 본인 생각이 조금 더 드러나도록 고민하면 좋을것 같아요. 예를 들어 네이밍에 대한 문제나, 특정 로직에 대해 바꿔보는거죠!

Important

초기 렌더시에만 mounted를 적용하려고 시도했던 것은 너무 좋습니다! 다만, 이런 것들을 검 해보면 더 좋을것 같아요. 현재 코드에서는 의도하신것처럼 동작하진 않네요ㅠㅠ

[타입스크립트]

전반적으로 타입스크립트가 약하다고 느껴졌어요. 타입에 대한 고민도 많이 해보면 좋을것 같아요. 지금은 타입을 위한 타입을 사용하는 느낌이 들었어요. 추가적으로 class와 typescript가 만나면 굉장한 oop 효과를 낼 수 있다고해요. 이러한 부분도 고민해보면 좋을것 같습니다

아래 코드를 사용할 때 dataset 속성을 태그마다 달아줘야 하는 게 비효율적이라고 생각해서 이벤트 위임 방식도 고려했지만, 이벤트 위임 방식보다 가독성이 좋을 것 같아 dataset 속성을 사용했습니다.

👉 HTML의 dataset은 HTML의 정보를 입력해 CSS, JS를 활용하는 목적에 있어요. 이걸 이벤트 위임 방식으로 한다는 것이 어떤건지 알려주실 수 있나요 ???


deleteItem(id: number) {
const items = [...this.state.items];
const index = items.findIndex(item => item.id === id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filter를 사용하는게 더 직관적일 것 같아요

메서드의 이름을 보면서 네이밍 유추가 잘되었지만, 코드레벨에서도 고민해보면 좋을것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

한 번에 1개만 삭제할 수 있기 때문에 findIndex 메서드를 쓰는 게 효율적이라고 생각합니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

한 번에 1개만 삭제할 수 있기 때문에 findIndex 메서드를 쓰는 게 효율적이라고 생각합니다.

취향차이지만, 매개변수로 id를 받고, id는 고유하기 때문에 1개만 필터링 되지않을까? 생각이 드네요!

}

toggleItem(id: number) {
const items = [...this.state.items];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 map을 사용하는게 조금 더 좋지 않을까? 라는 생각이 듭니다. map을 사용한다면 코드가 한줄로 표현도되지만, 선언적으로 작성하는 웹 개발 시대에는 지금처럼 부수효과를 일으키는 것보다 나은것 같아요!

물론! 깊은 복사를 하셔서 큰 상관은없지만 선언적이지 않다라고 느껴지네요

// your Code
import App from './App'

class Main {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main 클래스를 여러개의 인스턴스를 만들어도 이미 생성된 인스턴스를 가리키도록 싱글턴 패턴을 활용하면 좋지않을까 ? 라는 생각이드네요

Comment on lines 25 to 29
mounted() {
if (!this.isMounted) {
this.isMounted = true;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 코드는 불필요해 보여요. 왜냐하면 App에서 mounted로 실행을 하게되면, ItemAppender, Filter, Items들의 인스턴스가 생성됨과 동시에 mounted가 실행됩니다. 그리고 투두를 추가하면 render가 동작하면서 mounted를 호출하게 되면서 자식 컴포넌트들도 호출됩니다.

즉 의도하신건 "불필요한 렌더링을 줄이자" 인것 같은데, 의도하것처럼 동작하지가 않아요. ItemAppender에 console.log()를 출력해보시면 새로운 투두가 생길 때마다 로그가 찍히는게 보일거에요. 따라서 큰 의미 없는 코드인것 같아요.

하지만 이런걸 개선하려고 했던 건 너무나도 좋은것 같습니다. 저는 그런 생각 못했어요!

@Seoin02
Copy link
Collaborator Author

Seoin02 commented Dec 8, 2024

@D5ng 피드백 확인했습니다. 상세한 리뷰 감사해요!!
질문에 답을 하자면, 상위요소인 li태그에만 data-id를 지정하고, target.closest('li')로 요소를 찾아서 하위 요소인 토글 버튼과 삭제 버튼 이벤트를 처리하는 방식을 이벤트 위임 방식으로 생각했습니다. 이 방식보단 li, button 각각에 data-id를 다는 게 가독성이 좋을 것 같아서 해당 방식으로 구현했습니다.

@D5ng
Copy link
Contributor

D5ng commented Dec 8, 2024

@D5ng 피드백 확인했습니다. 상세한 리뷰 감사해요!! 질문에 답을 하자면, 상위요소인 li태그에만 data-id를 지정하고, target.closest('li')로 요소를 찾아서 하위 요소인 토글 버튼과 삭제 버튼 이벤트를 처리하는 방식을 이벤트 위임 방식으로 생각했습니다. 이 방식보단 li, button 각각에 data-id를 다는 게 가독성이 좋을 것 같아서 해당 방식으로 구현했습니다.

아하 이해했습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feat Feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants