Skip to content

Conversation

@ganeodolu
Copy link
Collaborator

@ganeodolu ganeodolu commented Jan 5, 2020

#7 미션002

구현

  • 비동기로 데이터 불러오기
  • 비동기로 데이터 추가하기
  • 비동기로 데이터 삭제하기
  • 비동기로 데이터 토글하기
  • on/offline을 고려하여 localStorage 활용하기

로토님의 미션4 boilerplate를 참고하여 작성하였습니다. 수업들을 때는 PR 올리지도 못했었는데 흉내라도 내었더니 뿌듯하면서도 갈 길이 구만리라 잘 갈 수 있을까?라는 걱정도 들었습니다.
아직 this, fetch 활용이 미숙한 상태로 기능구현만 하다보니 버그가 많이 있습니다.

질문 1.
App.js에서 import, export를 기능을 추가하기 위해 아래 코드와 같이 바꾸고,

export default async function app(){
 const data = await fetchData()
 this.data = data
......
}

아래와 같이 index.js에서 app을 import 후에 new app()을 사용하려고 하니,
import app from './App.js' new app() .....
app이 생성자가 아니라고 하여 실행이 되지 않았습니다. 그래서 app함수안에 (async function(){} 을 넣어서 해결하고자 하였으나 해결되지 않아서 index.js에서 new app()대신에 app()을 사용 후 App.js에서 this.data를 data로 모두 바꿨습니다. 결국 import 기능은 썼으나 생성자 기능은 버린 코드가 되었습니다. 둘 다 가능하게 만들 수 있을까요??

질문 2. 해결
할일 추가후 바로 토글이나 삭제를 누르면 id를 못 찾아서 실행이 안되는데, index는 맞으나 id가 맨 뒷자리만 이상한 녀석이 들어가 있었습니다. api 목록에는 있지도 않은 id인데 어떻게 불러오게 된 것인지 잘 이해가 되지 않았습니다.

TodoList.js에서 토글이나 삭제버튼을 눌렀을 때 this를 사용하지 않아서 발생한 문제였습니다. ㅎㅎ
const id = this.data[index]._id

질문 3. 해결
미션 1때 remote repository에 setting.json와 같이 필요없는 파일을 업로드시 제외하면 좋겠다라는 코멘트가 있어서 gitignore를 바꾸었더니 gitignore가 새로 업로드 되었습니다. 이게 맞는 건가요?? 😅

@YongHoonJJo
Copy link
Collaborator

방금 테스트 해봤는데, async function 으로는 new 키워드를 통한 호출이 안되는 것 같습니다..

Copy link
Owner

@eastjun-dev eastjun-dev left a comment

Choose a reason for hiding this comment

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

PR이 누적되면서 점점 발전해가는 호준님의 성실함에 박수를 보내드립니다 👏
코드도 확실히 PR1보다 깔끔해지셔서 코드 읽는데 수월했습니다 :) 몇가지 고려해주셨으면 하는것들 위주로 피드백 남겨드렸으니, 참고해주세요 :)

if (this === window) {
throw new Error(error.NO_USED_NEW_KEYWORD)
}
else if (Array.isArray(this.data) === false) {
Copy link
Owner

Choose a reason for hiding this comment

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

29 line의 else if와 컨벤션이 다른 것 같아요. 통일시켜 주시면 더 좋을 것 같습니다 :)
그리고 괄호안의 비교 내용은 (!Array.isArray(this.data)) 이렇게 줄여서 표현할 수 있을 것 같아요 :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

else if (Array.isArray(this.data) === false) 17줄
else if (className === 'destroy') 29줄
else if 컨벤션이 서로 어떻게 다른 건지 잘 이해가 안되는데 좀 더 설명해주실 수 있나요??
우선 17줄 괄호 else if 조건은 수정하였습니다.

@@ -0,0 +1,11 @@
export const error = {
Copy link
Owner

Choose a reason for hiding this comment

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

const error = {
  NO_USED_NEW_KEYWORD: "함수 선언시 new를 사용해주세요.",
  NOARRAY_DATA: "data타입이 Array가 아닙니다.",
  NOT_DATA: "data가 null 또는 undefined 입니다.",
  INVALID_DATA: "data타입이 문자열이 아닙니다.",
};

const USERNAME = 'ganeodolu'

const APIURL = `http://todo-api.roto.codes/${USERNAME}`

export { error, USERNAME, APIURL }

위와같이 정리하면, export를 여러번 코드에 넣지 않아도 될 것 같아요 :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

import뿐 아니라 export도 한번에 보낼 수 있군요!

this.render = function () {
const renderedHTMLText = this.data.map((val, idx) => {
if (!val.content) {
throw new Error(error.NOT_DATA)
Copy link
Owner

Choose a reason for hiding this comment

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

👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

수업때 사용하던 코드를 계속 재탕하고 있습니다.ㅎㅎ
이 상태로는 입력 data가 빈칸일때 에러메시지와 함께 함수를 빠져나오기 때문에
나중에 try catch로 메시지만 나오게 바꿔야할 것 같아요.

{
onAdd: async (todoText) => {
if(todoText.length > 0){
await fetch(APIURL,{
Copy link
Owner

Choose a reason for hiding this comment

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

post로 보내는 ajax요청의 json 구조는 나중에 다른 api가 추가되었을 때 재사용할 수 있게 따로 api.js파일로 분리해보면 어떨까요?
post관련 api가 추가되었을 때 매번 method, headers, body 구조를 동일하게 코딩해야한다면 중복이 늘어날 것 같아요 ㅠㅠ

@ganeodolu
Copy link
Collaborator Author

방금 테스트 해봤는데, async function 으로는 new 키워드를 통한 호출이 안되는 것 같습니다..

@YongHoonJJo 네 그래서 요런식으로 해보려고 했는데 아직 성공하지 못했어요.
export default function app(){
;(async function(){

1. 주석제거
2. 이벤트key 상수설정
3. ''로 통일
4. export 방법 수정
1. 통일된 메서드명으로 변경
2. 버튼 className확인시 swith문 사용
3. 불완전했던 할일 추가 뒤 완료, 삭제 기능 수정
Copy link
Collaborator

@StellaKim1230 StellaKim1230 left a comment

Choose a reason for hiding this comment

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

new app() 에서 생성자가 아니라 실행 안 된 이유는, App.js 가 async~await 구문을 사용하면서 return하는 값이 Promise여서 그런 것 같습니다. 아래 링크 참고하시면 될 것 같아요 ㅎ
참고

전, gitignore 사용할 때 아래 링크를 참고하는 편입니다.
참고

기타
끈기있는 호준님의 마인드와, 꼼꼼하게 기능 리뷰해주시는 점에 박수 드립니다!
동준님 말씀대로 점점 코드를 읽는데 수월해지는 기분도 들고, 호준님이 리뷰해주시는 덕분에 저도 배우는 점이 많은 것 같습니다! 같이 화이팅 합시다.

})

const $todoInput = document.querySelector('.new-todo')
const todoInput = TodoInput($todoInput,
Copy link
Collaborator

Choose a reason for hiding this comment

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

선언만 해놓고 사용하지 않는 함수인 것 같습니다! 다시 한 번 확인 부탁드려요^^

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

const todoInput은 필요없지만 TodoInput.js에서 할일 추가시 onAdd 메소드가 사용되고 있습니다. 다시 살펴보게 하는 리뷰 감사합니다. 👌

@@ -0,0 +1,11 @@
export default function renderedTemplate(val, idx) {
return `
<li ${val.isCompleted ? 'class="completed"' : ''} data-index=${idx}>
Copy link
Collaborator

Choose a reason for hiding this comment

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

indent 를 shift+tab 사용해서 앞으로 좀 떙기면 좀 더 보기 좋은 템플릿 구문이 될 것 같아요^^

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

평소에 비쥬얼스튜디오코드에서 crtl + k + f 로 자동정렬을 사용하는데 이건 찾아내질 못하네요. 수정하였습니다.

onAdd: async (todoText) => {
if (todoText.length > 0) {
await fetch(APIURL, {
method: 'POST',
Copy link
Collaborator

Choose a reason for hiding this comment

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

동준님 말씀대로 method, header, body 를 파라미터로 받는 함수를 만들어서 api.js 파일에서 공통적으로 사용 할 수 있게 관리해주면 더 좋은 코드가 될 것 같아요^^ 요 부분 까다로우실 것 같으면 동준님과 수요일에 페어프로그래밍할때 추천 드립니다^^

1. template분리
2. 주석제거
@ganeodolu
Copy link
Collaborator Author

new app() 에서 생성자가 아니라 실행 안 된 이유는, App.js 가 async~await 구문을 사용하면서 return하는 값이 Promise여서 그런 것 같습니다.
전, gitignore 사용할 때 아래 링크를 참고하는 편입니다.
참고

@StellaKim1230 생성자는 function을 Object로 변환하고, async는 function을 Promise generator로 바꾸기 때문에 생성자는 async와 같이 사용될 수 없다고 이해하면 되겠죠?? 덕분에 Promise에 대해서 공부하게 되었습니다. gitignore.io 도 잘 참고하겠습니다.

Copy link
Collaborator

@YongHoonJJo YongHoonJJo left a comment

Choose a reason for hiding this comment

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

고생하셨습니다. 다른분들의 피드백을 잘 반영하려고 하시는 게 많이 보입니다 ㅎㅎ

.gitignore 가 업데이트되면 새로 업로드 되는게 맞는 것 같습니다.!!

import { fetchData } from './api.js'

export default async function app(){
const data = await fetchData()
Copy link
Collaborator

Choose a reason for hiding this comment

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

이부분 때문에, async function 으로 선언하신 것 같은데, fetchData() 를 TodoList 안에서 호출하게 하면 되지 않을까요?

export default function TodoList(...) {
  // ...
  ;(async function() {
    const data = await fetchData()
    this.setState(data)
  })()
  // ...
}

이런식으로요..!!

Copy link
Collaborator

Choose a reason for hiding this comment

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

아니면, 생성자로 호출되었을때 안에서 this.render() 를 제거하고, App.js 제일 하단에서

;(async function() {
    const data = await fetchData()
    todoList.setState(data)
    todoCount.setState(data)
   // ...
  })()

이런식으로 하는 것도 괘찮을 것 같아요!

Copy link
Collaborator Author

@ganeodolu ganeodolu Jan 15, 2020

Choose a reason for hiding this comment

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

요 문제를 해결하고 싶은데 잘 안되네요. 용훈님 코드를 힌트 삼아서 도전해보겠습니다. 👍

Copy link
Collaborator Author

@ganeodolu ganeodolu Jan 15, 2020

Choose a reason for hiding this comment

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

두번째 방식을 참고하여 export default async function app()을 아래와 같이 변경하였습니다.

export default function app(){
    ;(async function(){
        const data = await fetchData()

data 선언 후 this.data = data를 넣어서 사용하고 싶었는데 async 안에는 this 사용이 안되는 것 같아요. this를 사용못하면 app도 생성자 기능을 못한다고 생각하여 new App()도 app()으로 변경하였습니다. app앞에 async를 삭제했다는 선에서 만족해야겠어요. 힌트 감사드립니다~

},
onClickFilter: (filterBoolean) => {
let filteredData = [...data]
filteredData = data.filter(todo => todo.isCompleted !== filterBoolean)
Copy link
Collaborator

Choose a reason for hiding this comment

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

filteredData 에 바로 데이터를 대입하기 때문에, let filteredData = [...data] 는 의미가 없어 보입니다!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

기존 코드를 그냥 지나쳐버렸네요. 수정하였습니다~

e.target.classList.add('selected');

if (className.includes('all')) {
onClickFilter()
Copy link
Collaborator

Choose a reason for hiding this comment

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

onClickFilter() 에 전달되는 값은 undefined, true, false 인가요..?
제가 잘 몰라서 그러는데, undefined 를 이렇게 의도적으로 전달하기도 하나요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

솔직히 저도 잘몰라서 이렇게 만들었어요. onClickFilter의 매개변수가 아닌 조건으로 필터링을 하는데, 전체목록을 나타내려면 필터조건이 아무것도 없어야해서 매개변수를 빈값으로 넣었습니다. 생각해보니 굳이 onClickFilter메소드를 사용하지 않고, 전체데이터를 렌더링해도 될 것 같아요.

if (!val.content) {
throw new Error(error.NOT_DATA)
}
else if (typeof (val.content) !== 'string') {
Copy link
Collaborator

Choose a reason for hiding this comment

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

위에서 throw 가 되면 이 아래로는 실행이 되지 않을텐데, else 는 생략해도 괜찮을 것 같아요!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

첫째 조건인 값은 있지만 문자가 아닌 경우에 에러를 나타내려고 else if를 사용했습니다. 결국 두가지 조건을 만족해야하기 때문에 else를 생략하면 좋겠다는 의미라고 봐도 될까요??

Copy link
Collaborator

Choose a reason for hiding this comment

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

if(...) throw 'something';
if(...) throw 'sth2'
첫번재 if 문의 조건을 만족하면 throw 가 되고 다음라인을 실행하지 않고,
만족하지 않으면 다음 라인을 실행하기 때문에, else 를 생략해도 된다는 의미였습니다!!

if(...) return a;
if(...) return b;
if(...) return a;
else if return b;

요것과 비슷한 케이스라고 보시면 될 것 같아요!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

추가답변이 있는지 몰라서 이제서야 확인을 하였습니다. 답변 감사드려요. 굳이 else if를 쓰지 않더라도 throw 때문에 함수가 중지되어 else를 안써도 된다고 이해하였는데 맞게 이해했는지 모르겠네요.
혹시 else를 생략했을 때 어떤 이점이 있는지 알려주실 수 있나요? 제가 두조건 사용시에는 else if를 문법처럼 사용하다보니 잘 납득이 안되서요. 아니면 이점과 상관없이 필요없는 상황이기 때문에 else가 군더더기 같은 존재여서 말씀해주신 것이라고 보면 될까요:grey_question:

throw new Error(error.NOT_DATA)
}
else if (typeof (val.content) !== 'string') {
throw new Error(error.INVALID_DATA)
Copy link
Collaborator

Choose a reason for hiding this comment

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

throw 을 하게 되면 이게 어디서 catch 가 되는건가요? ㅜ
그리고, 여러 데이터중에서 잘못된 데이터가 들어있을 확률이 있을지 모르겠습니다.
혹, 그런 데이터가 있다 하더라고, 에러를 던지기 보다는, filter 로 걸러내고, 올바른 데이터들에 대해서는 render 를 해야 하지 않을까...라는 생각을 합니다만, 다른분들의 의견도 궁금하네요 ㅜ

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

catch를 따로 만들지 않았어요. 에러검사도 수업때 사용하던 것이니 일단 넣어야지하고 사용했습니다. 저도 문제가 있다고 생각만하고 구현은 못했는데 정곡을 찔러주셨네요. 😅 데이터에 잘못된 값이 있을 경우 throw에러를 발생하면서 멈춰버리기 때문에 throw대신 에러만 알려주고, render시에 데이터를 검사하는 것은 이미 data에 들어와있기 때문에 입력 받을 때 미리 걸러야할 것 같아요. 날카로운 지적 감사합니다.

data 전개연산자 삭제
this를 사용하지 않아서 new로 선언할 이유가 없음
1. closest사용
2. 오프라인시 화면에 표시
API핸들러 만들면서 토글 및 삭제 불안정
필터선택후 토글 및 삭제는 안됨
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.

4 participants