39일차 TIL : 프로그래머스 / 리액트

변시윤·2022년 12월 8일
0

내일배움캠프 4기

목록 보기
40/131
post-custom-banner

학습내용

프로그래머스

  • 캐릭터의 좌표
  • 영어가 싫어요

리액트

  • 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만 주어집니다.

입출력 예

keyinputboardresult
["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;
}
  1. 시작 좌표 배열 location 생성
  2. for...of문으로 key 순회
  3. 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의 맨 앞에 올 수 없습니다.

입출력 예

numbersresult
"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);
}
  1. zero ~ nine까지의 문자열 배열 num 생성
  2. 인덱스를 반환하는 for문으로 num 돌리기
  3. numbersnum[i]번째 요소로 분리한 자리에 해당 인덱스 i 삽입
    numbers"onezeroseven"이라면 one0seven-10seven(6회)-107(4회)의 과정을 거쳐서 반환
  4. 결과값을 숫자형으로 반환

둘 다 넘 어려워서 다른 사람들 풀이를 참고했다.. 머쓱이 개싫어



리액트

todolist 보완

모든 컴포넌트에 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("빠트린 내용이 없나 다시 한 번 확인해보세요.");
    }
  };

조건문으로 datetodo 데이터가 모두 있을 때에만 리스트를 반환하고 인풋 박스 초기화, 하나라도 없다면 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여도 아래로 이동하지 않는 이슈 발생
  • Contents 컴포넌트에서 조건문 없이 단일한 <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를 구조분해할당으로 받는 과정에서 문제였던 건지...


계속 파다보면 언젠가는 해결할 수 있겠지만 이러다가는 진도를 못나가므로 일단은 여기서 스탑.... 투두리스트 너는 내가 레벨업 하고와서 조져준다...


styled-components

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를 전달함으로써 조건부 스타일링이 가능하다.

+ 전역 스타일(GlobalStyles)

모든 컴포넌트에 공통적으로 적용되는 스타일. 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;

+ CSS Nesting

[리액트 기초반] 2주차 - styled-components
4. SCSS 항목 참조


React Hooks

useState

가장 기본적인 hook으로 함수 컴포넌트에서 state를 가변적인 상태로 갖게 한다. 단, state가 원시 데이터타입이 아닌 객체 데이터 타입인 경우에는 불변성을 유지해야 한다.

state 관리를 위해 생성한 state/setStateprops로 전달할 수 있으며, 이렇게 전달받은 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씩 증가


useEffect

렌더링 될 때마다 특정 작업을 수행하도록 설정하는 Hook

import React, { useEffect } from "react";

const App = () => {

  useEffect(() => {
    console.log("hello useEffect");
  });

  return <div>Home</div>;
}

렌더링시 console.log("hello useEffect");가 자동 실행

의존성 배열(Dependency Array)

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는 최초에 한 번만 시도된다.

clean up

컴포넌트가 사라졌을 때 특정 작업을 실행

	useEffect(()=>{
	// 컴포넌트가 나타났을 때(mount) 실행
	return ()=>{
	// 컴포넌트가 사라졌을 때(unmount) 실행
		}
	}, [])

Strick Mode

애플리케이션 내의 잠재적인 문제를 알아내기 위한 도구. UI를 렌더링 하지 않고 하위 컴포넌트에 대한 부가적인 검사와 경고를 활성화 한다. 그렇기 때문에 삭제시에도 에러가 발생하지 않는다. 주로 index.js에서 쓰이지만 다른 컴포넌트에서 부분적으로 사용할 수도 있다.

잠재적인 문제

  • 안전하지 않은 생명주기를 사용하는 컴포넌트 발견
  • 레거시 문자열 ref 사용에 대한 경고
  • 권장하지 않는 findDOMNode 사용에 대한 경고
  • 예상치 못한 부작용 검사
  • 레거시 context API 검사

📌 참고 문헌


오늘 안에 리액트 숙련 주차 완강이 목표였었는데 투두리스트 기능 보완 부분에서 시간을 너무 많이 잡아먹는 바람에 리액트 훅스까지만 겨우 들었다. 그래도 몇 시간동안 투두리스트를 다시 살펴보면서 props와 자바스크립트에 조금 더 익숙해진 것 같다.

profile
개그우먼(개발을 그은성으로 하는 우먼)
post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 12월 9일

고생많으셨습니다 ㅎㅎ

답글 달기