티스토리 뷰

 

*피드백은 언제나 환영합니다🙌

오늘은 랜덤 명언 표시 기능을 다뤄보려고 한다.


먼저 QuotesDatabase.js 컴포넌트를 새로 만들었다. 그리고 Today.js 에 import 해주었다.

<div className='todayMainContent'>
    <QuotesDatabase />
    <p>완료한 작업 개수 : {done}</p>
</div>



그리고 명언과 작가가 담긴 배열을 만들었는데 너무 긴 명언은 뺐다.

const quotes=[
	{
	"quote": "You can observe a lot just by watching.",
	"author": "Yogi Berra"
	}, {
	"quote": "A house divided against itself cannot stand.",
	"author": "Abraham Lincoln"
	}, {
	"quote": "Difficulties increase the nearer we get to the goal.",
	"author": "Johann Wolfgang von Goethe"
	}, {
	"quote": "Fate is in your hands and no one elses",
	"author": "Byron Pulsifer"
	}, 
    .
    .
    .
]




난수 생성하기


- Math.random은 랜덤으로 숫자를 얻고 싶을 때 사용하는 함수다. 하지만 Math.random은 0.0과 같거나 크고 1.0보다 작은 값만을 출력하기 때문에 정수를 출력하고 싶다면 Math.floor(Math.random * 10)을 사용해야 한다.

- Math.floor() 함수는 소수점 1번째 자리를 버림하여 정수를 리턴하는 함수로, 정수인 난수를 생성하기 위한 방법으로 Math.random() 함수와 함께 쓰인다.


따라서 랜덤 quotes를 출력하려면 Math.floor()과 Math.random() 함수를 사용하면 된다.

const getRandomIndex = quotes[Math.floor(Math.random()*quotes.length)];



그리고 명언과 작가를 <span> 태그에 담아서 출력했다.

return (
	<div className='quotes'>
		<span>{getRandomIndex.quote}</span>
		<span>- {getRandomIndex.author}</span>
	</div>
)

 

끝!

인 줄 알았는데

에러떴다


에러 해결 과정: 동기와 비동기


 

Uncaught TypeError: Cannot read properties of null (reading 'map')

자바스크립트 에러 Top 10 중 첫번째가 떴다. 영광스럽다






먼저 코드를 살펴보자

Today.js

let [todos, setTodos] = useState(()=> 
	JSON.parse(window.localStorage.getItem("todayInLocal"))
);

useEffect(()=> {
    window.localStorage.setItem("todayInLocal", JSON.stringify(todos));
  }, [todos]);
 
 .
 .
 . 
return (
    <div className='inputButton'>
        <TodoInsert onAddTodo={addTodo} />
        <TodoList 
            todos={todos} 
            setTodos={setTodos} 
            onRemove={onRemove} 
            onToggle={onToggle} 
            addDone={addDone} 
            removeDone={removeDone} 
        />
    </div>
)

 

TodoList.js

function TodoList({todos, setTodos, onRemove, onToggle, addDone, removeDone}) {
  return(
    <div>
      {
        todos && todos.map(todo => (
            <TodoListItem 
              {...todo} 
              key={todo.id} 
              todos={todos} 
              setTodos={setTodos} 
              onRemove={onRemove} 
              onToggle={onToggle} 
              addDone={addDone} 
              removeDone={removeDone} 
            />         
          )
        )
      }
    </div>
  )
}

 

내가 작성한 코드를 설명하자면 다음과 같다.

1. Today.js의 useEffect() 내부에서 localStorage를 이용해 key값으로 todayInLocal을 넣어주었다.
2. 그리고 let [todos , setTodos] = useState()를 이용해 localStorage에서 저장한 값을 쓸 수 있도록 약간의 변경을 했다.
3. return에서 todos를 렌더링해서 화면에 보여준다. 이 과정에서 todos.map이 화면에 반환된다.


에러메세지를 대충 읽어보면 map에서 null값이 나왔다는 얘기같은데.. 저번엔 잘 작동했는데 왜 갑자기 에러가 난거지..? 황당하다.



동기와 비동기


 

여러가지를 검색하다가 답을 찾았는데 이 오류를 해결하기 위해선 비동기와 동기에 대해 알아야 한다.

동기(Synchronous) 처리 : 서버에 요청을 보냈을 때 응답이 돌아와야 다음 동작을 수행할 수 있다. 따라서 A 작업이 모두 진행될 때 까지 B 작업은 대기해야 한다.

비동기(asynchronous ) 처리: 서버에 요청을 보냈을 때 응답 상태와 상관없이 다음 동작을 수행할 수 있다. 따라서 A 작업이 시작하면 동시에 B 작업이 실행된다. A 작업은 결과값이 나오는대로 출력된다.



리액트로 다음과 같은 state를 만들었다고 해보자.

function App(){
  let [name, setName] = useState('kim')
}

 

setName을 사용하면 name이라는 state를 자유롭게 변경할 수 잇다. 하지만 setName() 같은 state 변경 함수들은 전부 비동기적으로 처리된다. 그래서 setName()을 처리하는데 오래 걸리면 다른 밑에 있는 코드들 부터 실행하기 때문에 문제가 생길 수 있다.


따라서 순차적으로 코드를 실행하고 싶을 때 useEffect를 사용하면 된다. (특정 state가 변경 될 때 useEffect를 실행할 수 있게 짜주면 됨!) 나는 여기까지는 올바르게 한 것 같다. 근데 문제는 useEffect를 이렇게 써도 처음 페이지 로드 될 때도 한 번 실행이 되기 때문에 의도치 않은 버그가 발생할 수 있다고 한다.
그래서 처음 페이지 로드 시 useEffect 실행을 막는 코드를 적용하던가 todos 라는 state를 활용하면 된다.

useEffect(()=> {
    window.localStorage.setItem("todayInLocal", JSON.stringify(todos));
  }, [todos]);


진짜 코딩애플은 최고다 리액트 강의도 재밌게 들었는데.. 감사합니다 감사합니다...



처음 페이지 로드했을 때 localStorage value 값을 봤더니 전부 null로 표시되어있었다. 이게 문제였군...



그래서 key 가 null 일 때는 (페이지 처음 로드되었을 때는) 코드가 동작하지 않게 조건을 추가했다.

우선 window.localStorage.getItem("todayInLocal")을 saved 변수에 담고 save가 null이 아닐 때 JSON.parse(saved)가 실행되도록 했다. 만약 saved가 null 이라면 빈 배열을 초기값으로 주도록 했다.
빈 배열 값을 준 이유는 null이나 undefined를 제외한 기본값으로 설정해야 자식 요소가 바르게 작동할 수 있기 때문이다.

let [todos, setTodos] = useState(()=> {
        const saved = window.localStorage.getItem("todayInLocal");
        if(saved !== null) {
            return JSON.parse(saved);
        } else {
            return [];
        }
    });



이렇게 했더니 다행히 오류가 사라지고 잘 작동됐다!! 동기와 비동기를 확실하게 짚고 넘어가서 오히려 다행이라고 생각한다.

동기, 비동기는 글로만 읽었을 때는 제대로 이해 못하고 감으로 넘겼었는데 이렇게 직접 겪으니까 무엇을 말하는지 알게됐다. 그리고 이런 에러 뜰 때마다 생각하는 건 자바스크립트 공부를 더 해야겠다는 것이다. 강의들을 때는 내가 어떤 부분이 부족한지 몰랐는데.. 여러모로 작은 프로젝트라도 해보는게 도움이 되는 것 같다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
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
글 보관함