일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 리스트 렌더링
- SCSS extend
- 프로그래머스 K_Digital Training
- flex
- 리액트
- 쌓임맥락
- 폼 입력 바인딩
- 다른컴퓨터에서 git사용
- KDT 프로그래머스
- 프로그래머스 프론트엔드 데브코스
- 이벤트 수식어
- SCSS import
- KDT 프로그래머스 데브코스 프론트엔드
- nextjs사용법
- vue 이벤트 수신
- netlify redirect
- Vue
- vuex map
- vue 지역 컴포넌트
- SCSS forward
- vue mixin
- postcss
- 프로그래머스 데브코스
- Spacer
- SCSS use
- 고양이 사진 검색기
- git 같은계정 다른 컴퓨터
- 프로그래머스 데브코스 프론트엔드
- react next
- intersection opserver
- Today
- Total
혼자 적어보는 노트
프로그래머스 데브코스 TIL - Day 69 본문
✅ 오늘의 학습
📌 React 심화(1)
- use ImperativeHandle
- 타입스크립트
useImperativeHandle
상위 컴포넌트에서 하위컴포넌트를 함수 호출로 제어할 수 있다
=> 자식에서 정의한 함수를 부모에서 사용할 수 있다.
const Input = (props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
clear: () => {
inputRef.current.
},
}));
return (
<>
<input ref={inputRef} />
</>
);
};
export default forwardRef(Input);
부모컴포넌트에서는 아래와 같이 전달해준다.
<button onClick={() => inputRef.current.clear()}>지우기</button>
보통 일반 컴포넌트에서 보다는 라이브러리에서 사용한다고 한다.
모듈의 구현과 나누는 방법
여기서 설명하는 모듈은 컴포넌트를 의미한다.
모듈을 잘 나누어서 구현하려면 응집도와 결합도가 중요하다.
1. 응집도
- 모듈 내 포함된 요소가 서로 연관되어 있는 정도.
- 높은 응집도일수록 좋은 설계
- 하나의 책임에 집중하고 독립성을 높이게되면 수정하기 위한 요소를 빠르게 찾을 수 있다.
높은 응집도를 구성하기 위해서는 아래의 두가지의 원칙을 따른다.
공통 폐쇄원칙
- 같은 이유로 동일한 시점에 변경되는 기능을 하나로 묶어야 한다는 원칙
- 객체지향 원칙인 단일 책임 원칙을 컴포넌트 관점으로 바라본 것
- 과하게 적용 시 재사용성이 줄어들 수 있다.
📁TodoList
ㄴindex.js
ㄴTodoListItem.js
하위 컴포넌트를 기능으로 본다면 컴포넌트끼리 묶을 수도 있다.
폴더를 모듈로 본다면 TodoList 안에 TodoListItem을 하나의 모듈로 묶을 수 있다.
TodoListItem를 TodoList라는 하나의 기능 안에서만 사용을 한다면 이렇게도 묶을 수도 있다.
공통 재사용 원칙
모듈 내의 기능들은 함께 재사용이 될 수 있어야 한다는 원칙
반대로 이야기하면 함께 재사용이 될 수 없다면 분리를 해야 한다는 뜻
과하게 적용 시 개발 용이성이 줄어들 수 있다.
📁ListItem
ㄴindex.js
📁TodoList
ㄴindex.js
TodoList 안에서 ListItem을 사용할 수도 있고 ListItem을 다른 컴포넌트에서도 사용할 수 있다면
이런식으로 분리해서 묶어놓을 수도 있겠다.
💡 공통 폐쇄 원칙을 적용하는 경우
적은 수의 모듈(컴포넌트)를 가지지만 재사용성이 낮다.
💡 공통 재사용의 원칙을 적용하는 경우
많은 수의 모듈(컴포넌트)를 가지지만 재사용성이 높다
=> 이 두가지 원칙을 골고루 사용해서 상황에 따라 사용하자!
그러면 결합도는 무엇일까?
2. 결합도
- 다른 모듈과의 의존성에 대한 정도
- 낮은 결합도일수록 좋은 설계
- 낮은 결합도일수록 안정성이 증가
즉, 결합도는 모듈이 다른 모듈들을 얼마나 의존하고 있는 지이다.
여러 컴포넌트를 의존하여 생성된 컴포넌트의 경우
다른 컴포넌트에서 수정이 발생하면 영향을 받게 된다.
위의 이론들을 바탕으로 여러 컴포넌트를 많이 만들면서 경험을 쌓아야겠다.
TypeScript
미리 다른 강의를 듣고 정리한 것이 있어서 문법은 생략하고
TodoList 를 따라 만들며 간단하게만 정리하기로 했다.
props 정의
전달되는 props는 interface로 지정할 수 있다.
interface Props {
id?: string;
content: string;
complete: boolean;
}
const Task = ({ id, content, complete, ...props }: Props) => {
return (
<div {...props}>
<Content complete={complete}>{content}</Content>
<button>Remove</button>
</div>
);
};
export default Task;
함수를 받아오는 경우 아래와 같이 이벤트를 등록해 준다.
interface Props {
on?: boolean;
onChange?(e: ChangeEvent): void;
}
styled로 전달된 props 정의
emotion styled로 만들어진 컴포넌트에 props로 받은 값을 사용할 때 타입을 지정할 수 있다.
const Content = styled.span<{ complete: boolean }>`
display: inline-block;
text-decoration: ${({ complete }) => (complete ? "line-through" : "none")};
`;
useToggle
반환되는 값이 함수가 섞여있기 때문에 Tuple을 통해 반환되는 배열의 형태를 정의 해 준다.
import { useCallback, useState } from "react";
const useToggle = (initialState: boolean = false): [boolean, typeof toggle] => {
const [state, setState] = useState(initialState);
const toggle = useCallback(() => setState((state) => !state), []);
return [state, toggle];
};
export default useToggle;
Context
context에 저장할 데이터도 interface를 통해 정의할 수 있다.
interface Task {
id: string;
content: string;
complete: boolean;
}
interface ITaskContext {
tasks: Task[];
addTask(content: string): void;
updateTask(id: string, status: boolean): void;
removeTask(id: string): void;
}
const TaskContext = createContext<ITaskContext>({} as ITaskContext);
interface의 이름 앞에 I를 붙이면 I다음에 나오는 객체에 대한 인터페이스라는 것을 의미한다.
컨텍스트를 생성할 때 값이 안들어가도 되지만 밑줄 에러가 생기기 때문에 as 를 사용하여 작성해 주어야 한다.
Provider
Provider의 경우 children만 받기 때문에 children의 타입을 지정해준다.
* ReactChild의 경우 deprecated 되어서 ReactNode로 대체했다.
import { createContext, ReactElement, useContext, useState } from "react";
interface Props {
children: ReactElement;
}
const TaskProvider = ({ children }: Props) => {
const [task, setTasks] = useState();
return <TaskContext.Provider value={{}}>{children}</TaskContext.Provider>;
};
export default TaskProvider;
useLocalstorage
전달받는 인자 중 initialValue의 경우 사용자 정의 값이기 때문에 제네릭을 사용하고
useToggle과 마찬가지로 Tuple을 사용하여 반환 값을 지정한다.
const useLocalStorage = <T>(key: string, initialValue: T): [T, typeof setValue] => {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (e) {
// console.error(error);
return initialValue;
}
});
const setValue = (value: T) => {
try {
const valueToStore = typeof value === "function" ? value(storedValue) : value;
setStoredValue(valueToStore);
localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (e) {
return initialValue;
}
};
return [storedValue, setValue];
};
export default useLocalStorage;
useLocalStorage를 사용할 땐 제네릭으로 작성한 부분에 대한 타입을 아래와 같이 작성한다.
const [tasks, setTasks] = useLocalStorage<Task[]>("task", []);
✍ 느낀 점
- 모듈을 효율적으로 구성하는 방식에 대해 고민을 했었는데 조금은 정리가 된 느낌이다.
다른데서 강의를 들었을 때 공통재사용 원칙만을 적용해서 작성하는 곳이 있었고
어떤 강의에서는 공통 폐쇄원칙만으로 작성을 하는 곳이 있어서 기준을 정하기가 애매했었다.
결국 상황에 따라 더 나은 것에 집중해서 사용하면 된다!
- Typescript의 경우 교육 과정에 포함되어 있지 않다고 생각했었는데 이번에 강의로 진행해주셔서 너무 좋았다.
물론 이전에 다른 강의들을 통해 접했지만 부족한 부분이 좀 더 채워진 느낌이다.
기존에 만들었던 컴포넌트들을 Typescript를 적용하여 하나씩 만들어 볼 예정이다!
'스터디' 카테고리의 다른 글
프로그래머스 데브코스 TIL - Day 71 (0) | 2022.06.29 |
---|---|
프로그래머스 데브코스 TIL - Day 70 (0) | 2022.06.25 |
프로그래머스 데브코스 TIL - Day 56 (0) | 2022.06.05 |
프로그래머스 데브코스 TIL - Day 55 (0) | 2022.06.03 |
프로그래머스 데브코스 TIL - Day 54 (0) | 2022.06.02 |