혼자 적어보는 노트

프로그래머스 데브코스 TIL - Day 51 본문

스터디

프로그래머스 데브코스 TIL - Day 51

jinist 2022. 5. 30. 23:12

 

✅ 오늘의 학습

📌 React (4)

 

- emoji 검색기 만들기

- Context API

- Upload 컴포넌트

- Badge 컴포넌트

- Icon 컴포넌트

 


 

😊 emoji 검색기 만들기

검색기를 만들땐 보통 api를 사용하지만 필요할 때마다 서버에 요청해서 데이터를 가져오는 방식이 아닌

데이터를 미리 한 곳에 작성해두고 불러오는 방식을 사용했다.

 

<Container>
  {emojis
    .filter((emoji) => emoji.title.indexOf(keyword) >= 0 || emoji.keywords.indexOf(keyword) >= 0)
    .slice(0, 10)
    .map((emoji) => (
      <EmojiListItem key={emoji.title} emoji={emoji} />
    ))}
</Container>

키워드에 맞게 emojis를 걸러내고 특정 개수로 자른 형태로 보여지게 만들었다.

 

Clipboard API를 통한 복사 기능

<ListItem onClick={() => navigator.clipboard.writeText(emoji.symbol)}>
</ListItem>

clipboard api의 존재를 몰랐었는데 이번에 알게 되었다.

 

clipboard api를 사용하면 웹 애플리케이션 내에서 잘라내기, 복사, 붙여넣기 기능을 구현하는데 사용할 수 있다.

 

 


context API

 

prop drilling을 해결하기 위한 도구중 하나.

컴포넌트의 tree레벨이 깊어졌을 때 prop을 일일이 거쳐서 넘기지 않고

한번에 바로 가져와서 사용할 수 있게 해준다.

 

Context API는 크게 전역 상태가 저장되는 context, 전역 상태를 제공하는 Provider,

그리고 전역 상태를 받아 사용하는 Consumer로 나뉘어져 있다.

 

* Provider가 변경되면 Consumer와 관련된 컴포넌트들이 전부 리렌더링된다. (최적화 필요)

* 컴포넌트가 독립적으로 존재할 수 없다는 이슈가 있다.

 

 

Provider 정의

import { createContext, useContext, useState } from "react";
import { v4 } from "uuid";

// Context 생성
const TaskContext = createContext();

// hook을 통해 Context 내보내기
export const useTasks = () => useContext(TaskContext);

// Provider 작성
const TaskProvider = ({ children }) => {
  const [tasks, setTasks] = useState([]);

  const addTask = (content) => {
    setTasks([
      ...tasks,
      {
        id: v4(),
        content,
        complete: false,
      },
    ]);
  };

  const updateTask = (id, status) => {
    setTasks(tasks.map((item) => (item.id === id ? { ...item, complete: status } : item)));
  };

  const removeTask = (id) => {
    setTasks(tasks.filter((item) => item.id !== id));
  };

  // value부분에서 제공할 데이터와 데이터를 변경하는 함수 전달
  return <TaskContext.Provider
          value={{ tasks, addTask, updateTask, removeTask }}>
          {children}
          </TaskContext.Provider>;
};

export default TaskProvider;

 

 

Provider 연결

최상위 컴포넌트인 App컴포넌트를 Context의  Provider로 감싸주면

하위 컴포넌트에서 데이터를 꺼내 쓸 수 있게 된다.

import TaskProvider from "./contexts/TaskProvider";

function App() {
  return (
    <TaskProvider>
      <Header />
      <NewTaskForm />
      <TaskList />
    </TaskProvider>
  );
}

export default App;

 

 

 

* styled로 정의된 컴포넌트에 props로 받은 데이터 사용하기

props로 받은 데이터에 따라 style을 변경할 수 있다.

const Content = styled.span`
  text-decoration: ${({ complete }) => (complete ? "line-through" : "none")};
`;

// ...

<Content complete={complete}>{content}</Content>

 

 


Upload 컴포넌트

 

children을 JSX를 반환하는 함수로 만들어서 file을 change했을 때

해당 file의 정보를 children에 추가할 수 있다.

 

return (
    <div onClick={handleChooseFile}>
      <Input ref={inputRef} type="file" name={name} accept={accept} onChange={handleFileChange} />
      {typeof children === "function" ? children(file) : children}
    </div>
  );
<Upload>
  {(file) => <button>{file ? file.name : "Click me~"}</button>}
</Upload>

처음엔 file이 없으니 clickme~ 버튼이 출력되고

onchange를 통해 file을 change하면 file의 값이 변경되어서 업로드한 file의 name을 출력시킬 수 있다.

 

이러한 형태의 컴포넌트를 만들어 본 적 없었는데

반환하는 함수를 사용하여 다르게 출력할 수 있다는 점이 굉장히 신기했다.

 

 

badge 컴포넌트

badge라는 변수를 사용하여 조건에 따라 다른 형태를 넣어줌으로써

재사용성이 높은 컴포넌트를 만들 수 있다.

const Badge = ({ children, showZero, dot = false, count, maxCount, backgroundColor, textColor, ...props }) => {
  const colorStyle = {
    backgroundColor,
    color: textColor,
  };

  let badge = null;
  if (count) {
    badge = <Super style={colorStyle}>{maxCount && count > maxCount ? `${maxCount}+` : count}</Super>;
  } else {
    if (count !== undefined) {
      badge = showZero ? <super style={colorStyle}>0</super> : null;
    } else if (dot) {
      badge = <Super className="dot" style={colorStyle} />;
    }
  }

  return (
    <BadgeContainer>
      {children}
      {badge}
    </BadgeContainer>
  );
};

 

return 부분에서 처리를 해 줄 수도 있겠지만

조건에 따라 변수에 담아서 출력하는 형태로 작성하니 가독성 측면에서 좋다고 느꼈다.

 

Icon 컴포넌트

icon컴포넌트에서 제공할 아이콘은 feather-icons 라는 오픈소스 아이콘에서 가져오기로 했다..

  const icon = require("feather-icons").icons[name];
  const svg = icon ? icon.toSvg(iconStyle) : "";
  const base64 = Buffer.from(svg, "utf8").toString("base64");

  return (
    <IconWrapper {...props} style={wrapperStyle}>
      <img src={`data:image/svg+xml;base64,${base64}`} alt={name} />
    </IconWrapper>
  );

feather-icon 라이브러리를 통해 아이콘을 받아오고 변환처리를 하여 image로 적용시켰다.

 

강의에서 다루진 않았지만 변환을 위해 buffer 패키지를 다운받아야 했어서

패키지 설명을 읽어보다 궁금해서 buffer에 대해 알아보았다.

 

buffer

Nodejs에서 buffer는 raw 바이너리 데이터를 저장할 수 있는 특수한 유형의 객체를 의미한다.

==> 왜 사용할까  

자바스크립트에서는 바이너리 데이터를 처리할 방법이 없었어서
바이너리 데이터를 처리하기 위해 nodejs에서 제공하는 기능이다.

 

nodejs에서 제공하는 기능이지만 패키지를 통해 buffer를 설치해서 사용할 수 있고

Buffer.from() 데이터를 넘기면 새로운 버퍼 객체를 만들어 낼 수 있다!

 

그러면 base64는 무엇?

8비트 이진 데이터(실행 파일이나, ZIP 파일 등)를 문자 코드에 영향을 받지 않는
공통 ASCII 영역의 문자들로만 이루어진 일련의 문자열로 바꾸는 인코딩 방식이라고 한다.

 

data urls를 사용하여 이미지를 생성할 때 base64인코딩 방식으로 소스를 넣는 방식을 사용한다.

그래서 변환이 필요했던 것!


data urls는 보통 아래와 같은 형태로 작성한다.

data:image/png;base64,[base64 코드 붙여넣기]

사용할 일이 아주 많지는 않지만 경우에 따라서 아주 유용할수 있다고 한다.

 

 


✍ 느낀 점

 

여러 컴포넌트들을 만드는 연습을 했는데 만들면서 흥미로웠던 부분이나 처음 보는 부분 위주로 정리를 했다.

하나의 컴포넌트를 만드는데도 여러가지를 고려해서 재사용성을 높이는 부분이 인상깊었다.

전부 storybook을 사용하면서 만들었는데 처음에는 몰랐지만 story들을 쌓아 나가니까

왜 사용하는지 알 듯 했다. 중간 팀 프로젝트에 적용할 지는 모르겠지만 강의를 통해

따라서 작성해보는 것이 아니라 직접 만들면서 활용을 해나가면 재밌을 것 같다는 생각을 했다..

직접 디자인한 ui들을 확인하고 모아놓는다면 꽤나 뿌듯할 듯..

Comments