Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3주차] 김선종 미션 제출합니다 #26

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

S-J-Kim
Copy link

@S-J-Kim S-J-Kim commented Apr 2, 2022

여러분 안녕하세요?

프론트엔드 멘토 김선종입니다.

사실 이번주는 과제를 안내려다가, 저도 좀 공부를 하고싶다는 생각에 부랴부랴 하루종일 과제를 해봤습니다.
저도 타입스크립트는 거의 걸음마 단계라, 참 어려운게 많더라구요. 여러분들도 절실히 느끼시지 않으셨나 싶네요. 동적 타이핑이라는게 러닝커브를 낮출 수 있는데에 큰 도움이 되는 것 같아요. 저한텐 아직 타입스크립트가 어렵네요.

아무래도 정적 타이핑이라는게, 기존에 존재하던 타입을 사용하는것도 있지만, 내가 직접 타입을 작성해줘야 하는 데에서 어려움이 온다고 생각을합니다. 게다가 리액트 자체는 타입스크립트를 고려하지 않고 설계되었기 때문에, 타입 체킹 부분에서 다들 많은 고생을 하셨을 것 같아요.

저는 이번주에 localStorage를 적용했습니다.
프짱님 리뷰 보시면 제가 상태 변경하면서 굳이 localStorage를 신경쓰지 않는 방법을 말씀드렸는데, 사실 이게 제가 어디서 보고 적은게 아니라 그 순간에 그냥 생각나는걸 적었어요. 그래서 그냥 나도 과제 해야겠다 하고 만들려니까,, 세상에 저는 useState를 안쓰고 useReducer를 썼더라구요. 그래서 인터넷에 예제를 좀 참고해서, useSyncedState, useSyncedReducer 두 훅을 만들어 봤습니다.

이 두개 커스텀 훅을 만들면서, 특히 제네릭 사용에 집중했었는데요, 타입스크립트를 사용하는 의미를 좀 갖는게 좋겠다 싶어서, localStorage에 저장되는 타입이 정해져 있어야 하겠다는 생각이 들었습니다. 그래서 제네릭 사용으로 타입도 명확하게 지키고 어느정도 재사용성도 높이게 된것 같네요.

저도 솔직히 여러분들께 도움이 되었으면 하는 마음에서 보여드리지만, 제가 또 여러분들보다 잘하고 잘난것도 아니기 때문에 (특히 타입스크립트는 더욱) 여러분들도 꼭 본인들이 이번주에 과제 수행하면서 공부했던 내용을 저에게 많이 공유해주셨으면 합니다. 서로 성장하자구요....^^

배포링크

Copy link

@sebastianrcnt sebastianrcnt left a comment

Choose a reason for hiding this comment

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

친애하는 선종님

선종님을 뵌지 이젠 1년이 넘는 시간이 지났군요
오랜만에 과거를 추억하며 코드 리뷰를 달아보았습니다

건강하시고 행복하세요

정시원 올림

import useTodoContext from '../hooks/useTodoContext';

const InputBox = () => {
const { addNewTodo } = useTodoContext();

Choose a reason for hiding this comment

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

Suggested change
const { addNewTodo } = useTodoContext();
const { addNewTodo } = useTodo();

내부 로직이 context를 이용해서 구현되었다는 점을 네이밍에 굳이 담지 않아도 괜찮을 것 같습니다!

import useForm from '../hooks/useForm';
import useTodoContext from '../hooks/useTodoContext';

const InputBox = () => {

Choose a reason for hiding this comment

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

React.FC로 타입을 지정해줍시다 ㅎㅎ


const InputBox = () => {
const { addNewTodo } = useTodoContext();
const { onSubmit, ...inputAttrs } = useForm('', addNewTodo);

Choose a reason for hiding this comment

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

spread operator를 이용해서 코드량을 줄이신 부분이 감동적입니다

Comment on lines +10 to +12
const handler = done ? undoneItem : doneItem;

handler(id);

Choose a reason for hiding this comment

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

doneItem, undoneItem은 명사니까, doItem 혹은 changeItemDoneStatus 등의 동사적 이름으로 함수를 이름 지었으면 좋았을 것 같습니다.

특히나 이 코드에서 handler의 타입이 불명확할뿐더러, undoneItem과 doneItem의 call signature가 달라질 경우 결국 분기해야 될 수도 있습니다. 따라서 이 경우는 다음과 같이 분리하는 것이 더 적합해 보입니다.

Suggested change
const handler = done ? undoneItem : doneItem;
handler(id);
if (done) {
changeItemDoneStatus(id, false)
} else {
changeItemDoneStatus(id, true)
}

이렇게 작성하는 것이 좀 더 쉽겠죠?

혹은 toggleItemDoneStatus(id)라는 함수를 만들어서

    toggleItemDoneStatus(id)

아예 한줄 짜리 코드로 바꿔보시는 것도 좋을 듯 합니다.

Copy link
Author

Choose a reason for hiding this comment

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

확실히 함수 이름짓는건 어렵습니다...

import { TodoAction } from '../Interfaces/actions';
import { ITodoCtx, TodoList } from '../Interfaces/interface';

const initialTodoList = [

Choose a reason for hiding this comment

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

Suggested change
const initialTodoList = [
const initialTodoList: TodoList = [

꼼꼼히 타이핑을 해줍시다. implicit any는 지양합시당

import { useState } from 'react';
import { IUseInputArg } from '../Interfaces/interface';

const useInput = (initialValue: IUseInputArg) => {

Choose a reason for hiding this comment

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

useForm과는 달리 useInput은 범용적으로 잘 설계한 것 같습니다. 다른 타입이 변해도 이 부분은 재사용 가능하기 때문이죠!

import SingleTodoItem from './SingleTodoItem';
import { ITodoContainerProps } from '../Interfaces/interface';

const TodoItemContainer = ({ type, items }: ITodoContainerProps) => {

Choose a reason for hiding this comment

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

Suggested change
const TodoItemContainer = ({ type, items }: ITodoContainerProps) => {
const TodoItemContainer: React.FC<ITodoContainerProps> = ({ type, items }: ITodoContainerProps) => {

Comment on lines +4 to +8
export type TodoItem = {
id: number;
content: string;
done: boolean;
};

Choose a reason for hiding this comment

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

Suggested change
export type TodoItem = {
id: number;
content: string;
done: boolean;
};
export interface TodoItem {
id: number;
content: string;
done: boolean;
};

interface가 조금 더 적합해 보입니다 ㅎㅎ

Copy link
Author

@S-J-Kim S-J-Kim Apr 5, 2022

Choose a reason for hiding this comment

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

저에게는 타입스크립트의 인터페이스가 자바의 interface같은 느낌이 너무 강하다보니,,,(단어에 어감때문이라도 더) 아무래도 이런건 type에 더 가깝지 않나? 라고 생각하게 만드는게 있나봅니다..

Comment on lines +11 to +16
return useReducer<Reducer<T, A>>((state: T, action: A) => {
const newState = reducer(state, action);
setValue(newState);

return newState;
}, storedValue!);

Choose a reason for hiding this comment

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

reducer typing이 꼼꼼하고 좋네요!

Comment on lines +3 to +18
const useSyncedState = <T>(
key: string,
initialState?: T
): [T, (value: T) => void] => {
const [storedValue, setStoredValue] = useState<T>(() => {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialState;
});

const setValue = (value: T) => {
setStoredValue(value);
localStorage.setItem(key, JSON.stringify(value));
};

return [storedValue, setValue];
};

Choose a reason for hiding this comment

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

state를 syncing 하는 함수를 만든 점이 인상깊습니다. 뭔가 나중에는 localStorage뿐만 아니라 백엔드와도 연결하기 쉽게 localStorage 말고 다른 저장소와도 저장 동기화를 할 수 있도록 설계해보면 좋을 것 같습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants