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

[2주차] 오대균 미션 제출합니다. #1

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

Conversation

oooppq
Copy link

@oooppq oooppq commented Sep 20, 2023

2주차 미션: React-Todo

배포

오대균의 React Todo List

사용법

  • 일반적인 한 줄 todo도 가능하지만, 첫 줄을 입력하고 enter 두 번 입력한 후 원하는 내용을 작성하면 제목과 본문 형태로 저장됩니다(git commit message처럼) .

  • 본문이 있는 todo는 (...) 같은 형태로 표시가 됩니다. 해당 todo를 클릭하면 디테일 정보를 확인할 수 있습니다.

  • 비밀번호를 설정할 수 있습니다. 화면 상단 우측에 톱니바퀴 모양을 누르면 비밀번호를 설정할 수 있고, 즉시 잠금이 해제됩니다.
    열려 있는 좌물쇠를 누르면 다시 잠기게 되고(로그아웃), 잠긴 좌물쇠를 누르고 비밀번호를 입력하면 잠금이 해제됩니다(로그인).
    잠금 해제를 하면, 다시 잠그기 전까지 5분간 해제 상태가 (새로고침 시에도) 유지됩니다.
    리셋 버튼을 통해 비밀번호를 초기화 할 수 있습니다.

  • 잠금이 해제된 유저(로그인한 유저)는 비밀글을 작성할 수 있습니다.
    todo 작성할 때 비밀 체크박스를 클릭하면 해당 글은 비밀글이 됩니다.
    잠금이 해제된 상태에서는 잘 보이지만, 잠김상태가 되면 내용을 확인할 수 없습니다.
    비밀번호를 초기화한다면 비밀글도 모두 삭제됩니다.

Key Features

기존

  • todo 추가, 제거, 완료
  • 중요도 및 기한 설정
  • 중요도 및 기한(시작일)을 기반으로 정렬된 list
  • 중요도에 따라 다른 글자색(중요도가 높을 수록 더 진하게)
  • 완료된 todo는 list의 가장 아래에 배치, 글자색 매우 연하게
  • 눈누 웹폰트 적용
  • localStorage를 사용하여 list 저장

추가

  • 본문 작성 가능
  • 본문 있는 게시물 클릭하면 detail 정보 확인 가능
  • 비밀번호 설정 기능
  • 비밀글 설정 기능
  • 모바일 환경에서도 모든 기능 이용할 수 있도록 사이즈에 맞게 대응
  • memo, useCallback을 사용한 렌더링 최적화

Key Questions

  • Virtual-DOM은 무엇이고, 이를 사용함으로서 얻는 이점은 무엇인가요?

    말 그대로 가상 DOM입니다. Virtual-DOM 자체가 무조건적으로 이로운 것은 아닙니다. React와 같은 SPA 형태의 웹에 state 변화에 따른 화면 변경이 빈번하게 발생하는 경우에 virtual-DOM의 효과가 발휘됩니다.

    상태 변화에 따라 직접적으로 DOM을 조작하게 된다면 불필요한 렌더링 과정이 발생하게 됩니다. 하지만, DOM을 가상화한 virtual-DOM 사용하면, 변경사항을 먼저 virtual-DOM에 반영하고(메모리에 가상화한 DOM이기 때문에 불필요한 업데이트가 발생하지 않음), virtual-DOM이 DOM과의 차이를 비교하며 변경사항을 업데이트 해주기 때문에 효율적으로 렌더링을 진행할 수 있게 됩니다.

  • 미션을 진행하면서 느낀, React를 사용함으로서 얻을수 있는 장점은 무엇이었나요?

    vanilla js로 무언가를 구현한다면, view와 logic의 관계를 개발자가 직접 설정해야 하고, 상태에 대한 변화를 직접 업데이트 해주어야 한다는 번거로움이 있습니다. React를 사용한다면, view와 logic의 관계를 크게 고려할 필요가 없고, react에서 기본적으로 제공하는 다양한 상태 관리 도구들을 통해서 쉽고 견고하게 상태를 관리할 수 있습니다.

  • React에서 상태란 무엇이고 어떻게 관리할 수 있을까요?

    상태는 데이터를 저장하는 하나의 객체입니다. 특별한점은 상태가 변경됐을 때, 컴포넌트가 리렌더링 된다는 것입니다. react에서는 useState라는 내장 hook을 제공하여 상태를 쉽게 관리할 수 있도록 합니다. useState의 return값에 상태변수와 setter 함수가 있는데, 상태변수에 상태가 저장되고, setter를 통해 상태를 조작할 수 있습니다. 상태를 안전하게 관리하기 위해 useEffect라는 hook도 제공하는데 상태의 변화를 감지하여 적절한 action을 취할 수 있도록 합니다. 상태를 조작하는 logic을 작성한 경우 useEffect 내부에서 사용하는 것이 경험상 안전한 것 같습니다.

전역적으로 상태를 관리하고 싶으면 상태관리 라이브러리를 추가적으로 사용해야 합니다.이번 과제에서는 useState만 사용해도 됐는데, 유저 인증 부분은 전역적으로 관리하는게 좋을 것 같아 zustand를 사용했습니다. 일반적으로 전역 상태관리를 위해 redux나 recoil을 많이 사용하는데, zustand는 작성해야 하는 보일러플레이트가 거의 없는 수준이라 한 번 써보시면 좋을 것 같습니다.

  • Styled-Components 사용 후기 (CSS와 비교)

    처음 react를 배우고 styling을 할 때 사용했던게 바로 styled-components였습니다. 컴포넌트 형태로 스타일링을 할 수 있다는 장점이 있고, props를 전달하여 다이나믹한 스타일 적용을 할 수 있다는 점, 스타일을 재활용하고 확장할 수 있다는 점이 메리트라고 생각합니다. css보다 개발자 친화적으로 코드를 작성할 수 있기 때문에 개발자들의 styling에 대한 진입장벽을 낮출 수 있는 라이브러리인 것 같고, 그렇기 때문에 지금까지 사용해왔습니다.
    그런데, 아래 문제에 대해서는 조금 생각을 해봐야할 것 같습니다.

    • styled-components를 사용한 styling은 번들링 과정에서 코드가 변환되는 과정을 거치게 될텐데, 성능적으로 좋은 영향은 아닐 것 같다.
    • 번들링되어 완성된 html을 봤을 때, class가 이상한 코드 형태로 지정되기 때문에 해당 element가 무슨 역할을 하는지 알기 쉽지 않다.
    • 스타일링 코드가 컴포넌트 형태로 작성되어야할 이유를 사실 잘 모르겠다.

    이러한 이유로, 최근들어 tailwindCSS를 사용하기 시작했는데, 상당히 편리하고 작성해야 하는 코드의 수도 매우 적어진 것 같아 만족하면서 사용하고 있습니다.

후기

어떤 라이브러리든간에 라이브러리를 사용하여 프론트를 구성하는게 편한 것 같습니다. 화면을 렌더링하거나 데이터를 상태 형태로 편하게 관리할 수 있도록 도와주기 때문에, 좀 더 편하게 앱의 기능 구현 자체에 몰입할 수 있었던 것 같습니다.

개발을 진행하다보니, 기능 구현 자체에 너무 몰두한 것 같은데, 스타일링이나 사용성을 좀 더 생각해봤으면 어땠을까 하는 생각이 듭니다.

추가하면 좋을 점

  • 유저 인증정보를 암호화하여 localStorage에 저장
  • 기깔나는 디자인..

컴포넌트 선언, 스타일 선언, hooks, utils, stores 디렉토리 추가
todo의 add, delete, state change 기능을 useTodo 라는 커스텀훅을 구성하여 구현했다.
todo에 대한 input을 받는 기능을 custom hook 형태로 구현했다.
각각의 input을 state 형태로 관리하고, onChangeHandler를 정의하여 변경사항 발생시 state를 업데이트 해주도록 구현했다.
todo element 컴포넌트 내부에 내용들을 배치하고, 본문이 있는 todo라면 detail 페이지를 띄울 수 있도록 작성했다.
todo list에서 done 버튼을 눌렀을 때, 기준에 맞게 정렬되도록 구현했어야 하는데,
깜빡하고 그냥 isDone만 바뀌도록 구현했어서, 이를 수정했다.
zustand를 사용하여 인증 state를 구현하고, 인증 관련 custom hook을 구성했다.
페이지 첫 렌더링시 인증 state를 구성하기 위한 AuthProvider 컴포넌트를 만들어 App 컴포넌트를
감싸주었다.
input에 비밀글 설정 checkbox를 추가했고, 비밀글인 todo는 비로그인시 안보이게 하는 기능을 추가했다.
비번을 초기화하고 비밀글을 모두 삭제한다.
전체 컨테이너, 헤더, todo element 디자인 적용 완료
input value를 state로 관리하면 리렌더링이 반복해서 발생하므로,
todo를 추가하는 컴포넌트를 분리한 후, memo와 useCallback을 사용하여
불필요한 리렌더링을 방지했다.
시작일 > 종료일이 되는 모든 경우를 차단했다.
Copy link

@wokbjso wokbjso left a comment

Choose a reason for hiding this comment

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

정말 많은 기능들을 구현하시고, 심지어 전역 상태 관리 라이브러리 없이 인증까지 구현하신 것을 보고 대균님의 열정에 많이 자극받고 배워갑니다~!! 수고하셨습니다!!

Copy link

Choose a reason for hiding this comment

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

index.css 파일에 reset css 를 적용하는 것도 좋지만 styled-components 에서 제공하는 createGlobalStyle 을 활용한다면 css 파일을 사용하지 않기 위해 CSS-IN-JS 를 사용하는 의도와도 좀 더 부합하지 않을까 생각합니다 ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

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

오 styled-comonents에서 그런 기능도 제공하는군요! 덕분에 좋은 기능 알아가요ㅎㅎ 다음에 사용해보겠습니다~~

Comment on lines +3 to +29

const AuthProvider = ({ children }) => {
const setAuth = useAuthStore((state) => state.setAuth);
const cachedAuth = JSON.parse(localStorage.getItem('auth') || 'null');
// 인증 상태가 설정될 때까지 isInit을 통해 children 렌더링 방지
const [isInit, setIsInit] = useState(true);

useEffect(() => {
if (!cachedAuth) setAuth({ authState: authStates.NOT_REGISTERED });
else if (
cachedAuth.authorizedDate + cachedAuth.duration <
new Date().getTime()
)
setAuth({
...cachedAuth,
authState: 'unauthorized',
});
else
setAuth({
...cachedAuth,
authState: 'authorized',
});
setIsInit(false);
}, [cachedAuth, setAuth]);

return <>{isInit ? null : children}</>;
};
Copy link

Choose a reason for hiding this comment

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

간단한 Todo 리스트 만들기가 과제였음에도 인증까지 구현하신 열정 배워갑니다!!

Comment on lines +51 to +55
const deleteTodo = (id) => {
const newTodos = todos.filter((todo) => todo.id !== id);

saveChanges(newTodos);
};
Copy link

Choose a reason for hiding this comment

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

지극히 개인적인 의견입니다만, todo 가 너무 많아지면 모든 todo를 비교하는 과정을 거쳐야돼서 아주 살짝 비효율적으로 작동할거 같다는 생각이 들어서 해당 todo의 index를 매개변수로 넘겨받아서 splice 함수로 처리해도 좋을 거 같다는 생각이 듭니다~!!

Copy link
Author

@oooppq oooppq Sep 23, 2023

Choose a reason for hiding this comment

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

음 시간복잡도는 O(n)으로 동일할 것 같아서 직관적으로 filter 함수를 사용해보았는데, 데이터가 커지면 splice가 조금 더 효율적일 수 있을 것 같아요 의견 감사합니다~~~

Copy link

Choose a reason for hiding this comment

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

시간복잡도에 대해서는 저도 더 공부가 필요한 부분인거같아요 ㅎㅎㅎ 뭐가 더 효율적일지 한번 같이 공부해봐요~~

Comment on lines +42 to +64

@media screen and (max-width: 500px) {
.date {
border-right: none;
}
.option {
border-bottom: 1px solid #bcbcbc;
}
}

@media screen and (max-width: 453px) {
.priority {
border-right: none;
}
}

@media screen and (max-width: 397px) {
.date {
border-right: none;
}
}
}
`;
Copy link

Choose a reason for hiding this comment

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

media query 적극 사용하시는 거 좋은 거 같습니다 ㅎㅎ

Comment on lines +19 to +28
// 브라우저에서 redux devtool로 상태변화를 확인할 수 있도록 devtools middleware 사용
export const useAuthStore = create(
devtools((set, get) => ({
auth: initialState,
setAuth: (payload) =>
set({
auth: payload ? { ...get().auth, ...payload } : { ...initialState },
}),
}))
);
Copy link

Choose a reason for hiding this comment

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

zustand 라이브러리는 처음 들어보는데 흥미롭네요 ㅎㅎㅎ 배워갑니다!!

Comment on lines +44 to +49
align-items: center;
border-top: 1px solid #bcbcbc;
padding: 10px 0;
border-bottom: 1px solid #bcbcbc;
font-size: 14px;
margin-bottom: 10px;
Copy link

Choose a reason for hiding this comment

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

자주 사용되는 색깔을 theme 파일에 정리한 후 styled-components 가 제공하는 ThemeProvider 를 사용하여 작업해준다면 재사용성이 더 좋아지지 않을까 생각합니다 ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

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

styled-components를 사용만 했지 부가기능에 대해선 크게 관심 없었는데 덕분에 많이 알아가게 되었어요!! 다음에 ThemeProvider 꼭 사용해보겠습니다~~

Copy link

@silverain02 silverain02 left a comment

Choose a reason for hiding this comment

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

안녕하세요 대균님! 리뷰파트너 이은비입니다
커스텀 훅부터 비밀글 설정까지 추가기능 구현해주신게 많아서 정말 많이 배워갈 수 있었습니다
다음과제도 화이팅해요~!

time,
mark,
audio,
video {

Choose a reason for hiding this comment

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

전체 요소에 기본 스타일링을 적용하시려는 거라면 * {} 전체선택자를 사용해 좀 더 간결하게 표현할 수 있을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

사실 귀찮아서 구글링한거 복붙한건데 다음에는 글케 표현해보겠습니다~~

justify-content: center;
}

@media screen and (max-width: 500px) {

Choose a reason for hiding this comment

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

저는 vw, %만 사용했는데 media query 를 사용하니까 확실히 모바일에서도 화면비율이 안 깨지는 것 같아요! 배워갑니다~!

newTodo.id = idx++;
newTodo.isDone = false;
let i = 0;
for (; i < todos.length; i++) {

Choose a reason for hiding this comment

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

저는 투두 항목들 정렬은 신경쓰지 않았는데 항목이 추가될 때마다 완료,우선순위,시작일 기준으로 정렬하는 방식이 나중에 렌더링할 때마다 재정렬할 필요도 없어서 효율적인 것 같아요!!

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.

3 participants