학습내용
프로그래머스
- 캐릭터의 좌표
- 영어가 싫어요
리액트
- todolist 보완
- styled-components
- React Hooks
- Strick Mode
문제 설명
머쓱이는 RPG게임을 하고 있습니다. 게임에는 up, down, left, right 방향키가 있으며 각 키를 누르면 위, 아래, 왼쪽, 오른쪽으로 한 칸씩 이동합니다. 예를 들어 [0,0]에서 up을 누른다면 캐릭터의 좌표는 [0, 1], down을 누른다면 [0, -1], left를 누른다면 [-1, 0], right를 누른다면 [1, 0]입니다. 머쓱이가 입력한 방향키의 배열 keyinput와 맵의 크기 board이 매개변수로 주어집니다. 캐릭터는 항상 [0,0]에서 시작할 때 키 입력이 모두 끝난 뒤에 캐릭터의 좌표 [x, y]를 return하도록 solution 함수를 완성해주세요.[0, 0]은 board의 정 중앙에 위치합니다. 예를 들어 board의 가로 크기가 9라면 캐릭터는 왼쪽으로 최대 [-4, 0]까지 오른쪽으로 최대 [4, 0]까지 이동할 수 있습니다.
제한사항
board은 [가로 크기, 세로 크기] 형태로 주어집니다.
board의 가로 크기와 세로 크기는 홀수입니다.
board의 크기를 벗어난 방향키 입력은 무시합니다.
0 ≤ keyinput의 길이 ≤ 50
1 ≤ board[0] ≤ 99
1 ≤ board[1] ≤ 99
keyinput은 항상 up, down, left, right만 주어집니다.입출력 예
keyinput board result ["left", "right", "up", "right", "right"] [11, 11] [2, 1] ["down", "down", "down", "down", "down"] [7, 9] [0, -4]
풀이
function solution(keyinput, board) {
let location = [0, 0];
for (key of keyinput) {
switch (key) {
case "up":
if (location[1] < board[1] / 2 - 1) location[1]++;
break;
case "down":
if (-location[1] < board[1] / 2 - 1) location[1]--;
break;
case "left":
if (-location[0] < board[0] / 2 - 1) location[0]--;
break;
case "right":
if (location[0] < board[0] / 2 - 1) location[0]++;
break;
}
}
return location;
}
location
생성for...of
문으로 key
순회switch
/case
문으로 4개의 key
에 해당되는 연산 실행down
만 5번이므로 [0,-5]
가 되어야 하지만, board
의 상하값이 9
이기 때문에 상하로 움직일 수 있는 최대 좌표 범위는 4.5, 즉 정수 4
부터 -4
까지다. 그러나 down
은 음수에 해당하기 때문에, (location[1] < board[1] / 2 - 1)
을 적용시 좌표값을 범위 이상으로 초과해버린다. 그렇기 때문에 location[1]
앞에 -
를 추가해서 양수 연산식으로 변환해야 한다.문제 설명
영어가 싫은 머쓱이는 영어로 표기되어있는 숫자를 수로 바꾸려고 합니다. 문자열 numbers가 매개변수로 주어질 때, numbers를 정수로 바꿔 return 하도록 solution 함수를 완성해 주세요.제한사항
numbers는 소문자로만 구성되어 있습니다.
numbers는 "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" 들이 공백 없이 조합되어 있습니다.
1 ≤ numbers의 길이 ≤ 50
"zero"는 numbers의 맨 앞에 올 수 없습니다.입출력 예
numbers result "onetwothreefourfivesixseveneightnine" 123456789 "onefourzerosixseven" 14067
풀이
function solution(numbers) {
const num = ["zero","one","two","three","four","five","six","seven","eight","nine"];
for (i in num) {
numbers = numbers.split(num[i]).join(i);
}
return Number(numbers);
}
zero
~ nine
까지의 문자열 배열 num
생성for
문으로 num
돌리기numbers
를 num[i]
번째 요소로 분리한 자리에 해당 인덱스 i
삽입numbers
가 "onezeroseven"
이라면 one0seven
-10seven
(6회)-107
(4회)의 과정을 거쳐서 반환둘 다 넘 어려워서 다른 사람들 풀이를 참고했다.. 머쓱이 개싫어
모든 컴포넌트에
props
구조 분해 할당 적용
const addList = () => {
const newList = {
id: list.length + 1,
date: date,
todo: todo,
isDone: false,
};
if (date && todo) {
setDate("");
setTodo("");
setList([...list, newList]);
} else {
alert("빠트린 내용이 없나 다시 한 번 확인해보세요.");
}
};
조건문으로 date
와 todo
데이터가 모두 있을 때에만 리스트를 반환하고 인풋 박스 초기화, 하나라도 없다면 alert
반환
delete
버튼 클릭시 confirm()
으로 확인 const deleteList = (id) => {
const newList = list.filter((todo) => todo.id !== id);
window.confirm("정말 삭제하시겠습니까?")
? setList(newList)
: list.map(() => {});
};
삼항연산자로 true일시 삭제 후의 리스트, false일시 리스트 전체를 반환
인풋 박스 빈칸 alert
후에 인풋박스 자동 포커스
DOM과 useRef
로 접근 시도해봤으나 실패
Contents 컴포넌트 return
중복 코드 제거
if
/else if
문으로 (!todo.isDone)
과 (todo.isDone)
의 <TodoList>
를 반환isDone
이 true여도 아래로 이동하지 않는 이슈 발생<TodoList>
를 반환 후 Todolist 컴포넌트에서 조건에 따라 다른 className
적용<div>
태그 반환 불가 Warning: Each child in a list should have a unique "key" prop. Check the render method of `Contents`. See https://reactjs.org/link/warning-keys for more information. at TodoList (http://localhost:3000/static/js/bundle.js:290:5) at Contents (http://localhost:3000/static/js/bundle.js:150:5) at div at App (http://localhost:3000/static/js/bundle.js:38:74)
데이터와 기능은 정상적으로 동작하지만 렌더링과 동시에 콘솔창에 전에 없던 에러가 떴다. Contents 컴포넌트가 문제인 건 알겠는데 여기저기 수시로 수정했더니 정확히 뭐가 원인인지 모르겠다ㅜㅜ 전달받은 props
를 구조분해할당으로 받는 과정에서 문제였던 건지...
계속 파다보면 언젠가는 해결할 수 있겠지만 이러다가는 진도를 못나가므로 일단은 여기서 스탑.... 투두리스트 너는 내가 레벨업 하고와서 조져준다...
CSS-in-JS(자바스크립트로 CSS 코드를 작성하는 방식)로 컴포넌트를 꾸미는 리액트 패키지. styled-components 역시 이름처럼 컴포넌트에 해당된다.
yarn add styled-components
const StBox = styled.div`
width: 100px;
height: 100px;
border: 1px solid ${(props) => props.borderColor};
margin: 20px;
`;
const boxList = ["red", "green", "blue"];
const getBoxName = (color) => {
switch (color) {
case "red":
return "빨간 박스";
case "green":
return "초록 박스";
case "blue":
return "파란 박스";
default:
return "검정 박스";
}
};
const App = () => {
return (
<StContainer>
{boxList.map((box) => (
<StBox borderColor={box}>{getBoxName(box)}</StBox>
))}
</StContainer>
);
};
자바스크립트 기반이기 때문에 위와 같이 스타일을 적용하는 동시에 메서드를 사용할 수 있다. 또한 컴포넌트에 해당되므로 props
를 전달함으로써 조건부 스타일링이 가능하다.
모든 컴포넌트에 공통적으로 적용되는 스타일. Styled-components에서 제공하는 함수
createGlobalStyle()
을 사용한다.
styled-reset
css를 초기화하는 작업으로 주로 전역 스타일과 함께 쓰인다.
yarn add styled-reset
import { createGlobalStyle } from "styled-components";
import reset from "styled-reset"
const GlobalStyle = createGlobalStyle`
${reset}
body {...}
`;
export default GlobalStyle;
[리액트 기초반] 2주차 - styled-components
4. SCSS 항목 참조
가장 기본적인 hook으로 함수 컴포넌트에서 state를 가변적인 상태로 갖게 한다. 단, state가 원시 데이터타입이 아닌 객체 데이터 타입인 경우에는 불변성을 유지해야 한다.
state 관리를 위해 생성한
state
/setState
는props
로 전달할 수 있으며, 이렇게 전달받은setState
는 상위 컴포넌트의 state까지도 변경할 수 있다. 단, 이런 방식이 많아지면 프로젝트 관리가 어렵기 때문에 redux와 같은 state 관리툴을 사용하는 편이 좋다.
일반 업데이트
setState(number + 1);
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
<div>{number}</div>
<button
onClick={() => {
setNumber(number + 1); // 첫번째 줄
setNumber(number + 1); // 두번쨰 줄
setNumber(number + 1); // 세번째 줄
}}
>
버튼
</button>
</div>
);
}
batch 처리로 인해 setNumber()
명령을 하나로 모아 최종적으로 한 번만 실행하기 때문에 버튼 클릭시 1씩 증가
함수형 업데이트
setState((currentNumber)=>{ return currentNumber + 1 });
state
를 인자로 받아 인자를 변경하는 함수를 작성하는 방식
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
<div>{number}</div>
<button
onClick={() => {
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
}}
>
버튼
</button>
</div>
);
}
setNumber()
명령을 모두 모아 순차적으로 한 번씩 실행시키므로 {number}
에 값이 누적되므로 버튼 클릭시 3씩 증가
렌더링 될 때마다 특정 작업을 수행하도록 설정하는 Hook
import React, { useEffect } from "react";
const App = () => {
useEffect(() => {
console.log("hello useEffect");
});
return <div>Home</div>;
}
렌더링시 console.log("hello useEffect");
가 자동 실행
const App = () => {
const [value, setValue] = useState("");
useEffect(() => {
console.log("hello useEffect");
});
return (
<div>
<input
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value);
}}
/>
</div>
);
}
렌더링이 될 때마다 실행되는 useEffect
특성상 인풋박스의 값을 실시간으로 반영하는 위 코드의 경우, input
에 글자를 입력할 때마다 useEffect
가 실행된다. 의존성 배열을 사용하면 이런 현상을 방지할 수 있다.
useEffect(() => {
console.log("hello useEffect");
}, []);
{중괄호}
다음에 들어오는 배열이 의존성배열인데 의존성배열을 빈 배열로 설정하면 해당 배열에 값이 들어올 때만 useEffect
를 실행한다. 즉, 배열 안에 아무것도 없기 때문에 useEffect
는 최초에 한 번만 시도된다.
컴포넌트가 사라졌을 때 특정 작업을 실행
useEffect(()=>{ // 컴포넌트가 나타났을 때(mount) 실행 return ()=>{ // 컴포넌트가 사라졌을 때(unmount) 실행 } }, [])
애플리케이션 내의 잠재적인 문제를 알아내기 위한 도구. UI를 렌더링 하지 않고 하위 컴포넌트에 대한 부가적인 검사와 경고를 활성화 한다. 그렇기 때문에 삭제시에도 에러가 발생하지 않는다. 주로 index.js에서 쓰이지만 다른 컴포넌트에서 부분적으로 사용할 수도 있다.
잠재적인 문제
- 안전하지 않은 생명주기를 사용하는 컴포넌트 발견
- 레거시 문자열
ref
사용에 대한 경고- 권장하지 않는
findDOMNode
사용에 대한 경고- 예상치 못한 부작용 검사
- 레거시
context API
검사
📌 참고 문헌
오늘 안에 리액트 숙련 주차 완강이 목표였었는데 투두리스트 기능 보완 부분에서 시간을 너무 많이 잡아먹는 바람에 리액트 훅스까지만 겨우 들었다. 그래도 몇 시간동안 투두리스트를 다시 살펴보면서 props
와 자바스크립트에 조금 더 익숙해진 것 같다.
고생많으셨습니다 ㅎㅎ