[TIL] 230528

먼지·2023년 5월 28일
0

TIL

목록 보기
55/57
post-thumbnail

프로그래머스 입문 Day 23

배열, 정렬, 문자열

특이한 정렬

문제 설명
정수 n을 기준으로 n과 가까운 수부터 정렬하려고 합니다. 이때 n으로부터의 거리가 같다면 더 큰 수를 앞에 오도록 배치합니다. 정수가 담긴 배열 numlist와 정수 n이 주어질 때 numlist의 원소를 n으로부터 가까운 순서대로 정렬한 배열을 return하도록 solution 함수를 완성해주세요.

function solution(numlist, n) {
    return numlist.sort((a, b) => {
        const [aGab, bGab] = [Math.abs(a - n), Math.abs(b - n)]
        return aGab === bGab ? b - a : aGab - bGab;
    })
}

등수 매기기

문제 설명
영어 점수와 수학 점수의 평균 점수를 기준으로 학생들의 등수를 매기려고 합니다. 영어 점수와 수학 점수를 담은 2차원 정수 배열 score가 주어질 때, 영어 점수와 수학 점수의 평균을 기준으로 매긴 등수를 담은 배열을 return하도록 solution 함수를 완성해주세요.

function solution(score) {
  	let avg = score.map(([a, b]) =>(a + b) / 2);
    let sorted = avg.slice().sort((a, b) => b - a);
    return avg.map(v => sorted.indexOf(v) + 1);
}

// 다른 사람의 풀이
function solution(score) {
  return score.map((el) => {
    return (
      score.filter((v) => (v[0] + v[1]) / 2 > (el[0] + el[1]) / 2).length + 1
    );
  });
}

옹알이 (1)

문제 설명
머쓱이는 태어난 지 6개월 된 조카를 돌보고 있습니다. 조카는 아직 "aya", "ye", "woo", "ma" 네 가지 발음을 최대 한 번씩 사용해 조합한(이어 붙인) 발음밖에 하지 못합니다. 문자열 배열 babbling이 매개변수로 주어질 때, 머쓱이의 조카가 발음할 수 있는 단어의 개수를 return하도록 solution 함수를 완성해주세요.

function solution(babbling) {
  var answer = 0;
  let arr = ["aya", "ye", "woo", "ma"];
  for (let i in babbling) {
    let word = babbling[i];
    for (let j in arr) {
        word = word.replaceAll(arr[j], ' ');   
        console.log(word)
    }
    if(word.trim().length === 0) {
        console.log(word.trim())
        answer++;
    }
  }
  return answer;
}

// 다른 사람의 풀이
function solution(babbling) {
  var answer = 0;
  const regex = /^(aya|ye|woo|ma)+$/;

  babbling.forEach(word => {
    if (regex.test(word)) answer++;  
  })

  return answer;
}

문자열 관련 문제는 정규식을 잘하면 수월할 것 같음,,

로그인 성공?

문제 설명
머쓱이는 프로그래머스에 로그인하려고 합니다. 머쓱이가 입력한 아이디와 패스워드가 담긴 배열 id_pw와 회원들의 정보가 담긴 2차원 배열 db가 주어질 때, 다음과 같이 로그인 성공, 실패에 따른 메시지를 return하도록 solution 함수를 완성해주세요.

아이디와 비밀번호가 모두 일치하는 회원정보가 있으면 "login"을 return합니다.
로그인이 실패했을 때 아이디가 일치하는 회원이 없다면 “fail”를, 아이디는 일치하지만 비밀번호가 일치하는 회원이 없다면 “wrong pw”를 return 합니다.

// 처음부터 answer을 'fail'로 잡으면 if 문을 하나 없앨 수 있을 텐데!
function solution(id_pw, db) {
    const [id, pw] = id_pw;
    let answer = ''
    for(let [dbId, dbPw] of db) {
        if(dbId === id && dbPw === pw) answer = 'login'
        if(dbId === id && dbPw !== pw) answer = 'wrong pw'
        if(dbId !== id && dbPw !== pw) answer = 'fail'
    }
    return answer;
}

// 다른 사람의 풀이
function solution(id_pw, db) {
  const [id, pw] = id_pw;
  const map = new Map(db);
  return map.has(id) ? (map.get(id) === pw ? 'login' : 'wrong pw') : 'fail';
}

리액트 공식문서

Reacting to Input with State

선언형 UI와 명령형 UI의 차이점

  • 명령형 프로그래밍에선 일어나는 일에 따라 UI를 조작하기 위한 정확한 지침을 작성해야 함
  • 운전자는 사용자가 어디로 가고 싶은지 모른 채 명령만 따를 뿐 (지시를 잘못 내리면 엉뚱한 곳에 가게 됨) 컴퓨터에게 각 요소에 "명령"을 내려 어떻게 UI를 업데이트할 내용을 지시해야 하므로, 이를 명령형이라고 부름
  • 단순 고립된 예제에선 충분히 잘 동작하지만, 더 복잡한 시스템에선 관리하기 어려워짐. 리액트는 이런 문제를 해결하기 위해 만들어진 것!
  • 직접 UI를 조작하지 않음. 대신 표시할 내용을 선언하면 React가 UI를 업데이트할 방법을 알아냄.

브라우저 빌트인 DOM을 사용한 명령형 UI 프로그래밍 예시

async function handleFormSubmit(e) {
  e.preventDefault();
  disable(textarea);
  disable(button);
  show(loadingMessage);
  hide(errorMessage);
  try {
    await submitForm(textarea.value);
    show(successMessage);
    hide(form);
  } catch (err) {
    show(errorMessage);
    errorMessage.textContent = err.message;
  } finally {
    hide(loadingMessage);
    enable(textarea);
    enable(button);
  }
}

function handleTextareaChange() {
  if (textarea.value.length === 0) {
    disable(button);
  } else {
    enable(button);
  }
}

function hide(el) {
  el.style.display = 'none';
}

function show(el) {
  el.style.display = '';
}

function enable(el) {
  el.disabled = false;
}

function disable(el) {
  el.disabled = true;
}

function submitForm(answer) {
  // Pretend it's hitting the network.
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (answer.toLowerCase() == 'istanbul') {
        resolve();
      } else {
        reject(new Error('Good guess but a wrong answer. Try again!'));
      }
    }, 1500);
  });
}

let form = document.getElementById('form');
let textarea = document.getElementById('textarea');
let button = document.getElementById('button');
let loadingMessage = document.getElementById('loading');
let errorMessage = document.getElementById('error');
let successMessage = document.getElementById('success');
form.onsubmit = handleFormSubmit;
textarea.oninput = handleTextareaChange;

UI를 선언적인 방식으로 생각하기

Step 1: 컴포넌트의 다양한 시각적 상태 식별하기

  • 사용자에게 표시될 수 있는 UI의 다양한 "상태"를 모두 시각화
    - Empty, Typing, Submitting, Success, Error

Step 2: 상태 변화를 촉발하는 요소를 파악하기

  • 두 종류의 입력에 대한 응답으로 상태 변경을 촉발할 수 있음
    - 사람의 입력: 버튼 클릭, 필드 입력, 링크 이동 등
    • 컴퓨터의 입력: 네트워크에서 응답 도착, 시간 초과, 이미지 로딩 등
      - 두 경우 모두 state 변수를 설정해야 UI를 업데이트할 수 있음
  • 흐름을 시각화하는 방식으로 구현하기 훨씬 전에 버그를 분류할 수 있음!

Step 3: useState를 사용하여 메모리의 상태를 표현하기

  • 각 상태 조각은 “움직이는 조각”이며, 가능한 적은 수의 “움직이는 조각”을 원함. 복잡할수록 버그가 더 많이 발생함!
  • 반드시 필요한 state부터 시작
  • 그 담엔 시각적 상태 중 어떤 상태를 표시할지를 나타내는 state 변수를 추가
  • 가능한 모든 시각적 상태를 확실히 다룰 수 있을 만큼 추가 -> 최선이 아니어도 괜찮음. 리팩토링 하는 것도 과정의 일부!

Step 4: 비필수적인 state 변수를 제거하기

  • 중복을 줄이고 의도하지 않은 경우를 피하기.. 목표는 state가 사용자가에게 보여주기를 원하는 유요한 UI를 나타내지 않는 경우를 방지하는 것 (예를 들어, 오류 메세지를 표시하면서 동시에 입력을 비활성화하면 사용자는 오류를 수정할 수 없게 됨)
    - state가 모순을 야기하나?
    • 다른 state 변수에 이미 같은 정보가 있나?
    • 다른 state 변수를 뒤집으면 동일한 정보를 얻을 수 잇나?

Step 5: 이벤트 핸들러를 연결하여 state를 설정하기

  • 마지막으로 state 변수를 설정하는 이벤트 핸들러를 생성함.

모든 이벤트 핸들러가 연결된 최종 form

// App.js
import { useState } from 'react';

export default function Form() {
  const [answer, setAnswer] = useState('');
  const [error, setError] = useState(null);
  const [status, setStatus] = useState('typing');

  if (status === 'success') {
    return <h1>That's right!</h1>
  }

  async function handleSubmit(e) {
    e.preventDefault();
    setStatus('submitting');
    try {
      await submitForm(answer);
      setStatus('success');
    } catch (err) {
      setStatus('typing');
      setError(err);
    }
  }

  function handleTextareaChange(e) {
    setAnswer(e.target.value);
  }

  return (
    <>
      <h2>City quiz</h2>
      <p>
        In which city is there a billboard that turns air into drinkable water?
      </p>
      <form onSubmit={handleSubmit}>
        <textarea
          value={answer}
          onChange={handleTextareaChange}
          disabled={status === 'submitting'}
        />
        <br />
        <button disabled={
          answer.length === 0 ||
          status === 'submitting'
        }>
          Submit
        </button>
        {error !== null &&
          <p className="Error">
            {error.message}
          </p>
        }
      </form>
    </>
  );
}

function submitForm(answer) {
  // Pretend it's hitting the network.
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      let shouldError = answer.toLowerCase() !== 'lima'
      if (shouldError) {
        reject(new Error('Good guess but a wrong answer. Try again!'));
      } else {
        resolve();
      }
    }, 1500);
  });
}

SSR SSG ISR

처음 사용자가 빠르게 페이지를 보게 해주고 싶다!

SSR

  • 매번 페이지를 그림
  • 최신화된 데이터를 항상 사용자 맞춤형으로 그려줄 수도 있음
  • +CSR(사용자 동작에 따라 움직이는 것들)
    - 가상돔을 HTML 문자열로 만들어 브라우저가 파싱 -> 돔 객체로 만들어 화면에 그림 -> 자바스크립트와서 하이드레이션 -> 리액트가 돔을 조작해서 화면을 그림
    - 사용자마다 다른 페이지를 보여줘야 함
    • 둘을 섞어서 쓰자!

SSG

  • 미리 데이터를 불러오고, 페이지를 그려서 HTML 파일로 구워둔다

  • 비용도 적게 들고 성능도 괜찮음

  • 사용자 맞춤 & 데이터를 최신으로 유지하기 힘듦 (조금만 바뀌어도 빌드를 모두 다시 해야 함)

  • 처음에 딱 첫 페이지를 그리고 빠르게 보여주고 검색엔진에도 걸렸으면 좋겠다

  • 데이터가 없어 로딩이 필요 없는 애들. 애초에 가져오는 데이터가 없어서 다 똑같이 보니까 사용자마다 다르지 않음. => 로그인/회원가입, 상세페이지

  • SSR, SSG
    - 사용자가 처음에 볼 것만, 첫 데이터만 넣고 리액트쿼리에게 맡기기
    - 얘넨 알아서 불러오고 스크로해야할거나 2페이지는 클라이언트에서 그리기

ISR (Incremental Static Regeneration)

  • 점진적으로 정적 페이지 다시 생성
  • 변경 페이지만 다시 그리는 것!
export default async function handler(req, res) {
  // Check for secret to confirm this is a valid request
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' });
  }
 
  try {
    // this should be the actual path not a rewritten path
    // e.g. for "/blog/[slug]" this should be "/blog/post-1"
    await res.revalidate('/path-to-revalidate');
    return res.json({ revalidated: true });
  } catch (err) {
    // If there was an error, Next.js will continue
    // to show the last successfully generated page
    return res.status(500).send('Error revalidating');
  }
}
profile
꾸준히 자유롭게 즐겁게

0개의 댓글