프로그래머스 Lv.3 입국심사와 이진탐색

해결하지 못하여서 구글링하였다.
나는 완전탐색, 그리디, 이진탐색 문제들에 약한 것 같다.

입국심사

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def solution(n, times):
answer = 0;
left,right=1,max(times)*n; # left는 최소시간 right는 최대시간
while(left<=right):
mid=(left+right)//2;
people=0;
for time in times:
people += mid //time; # mid 시간동안 심사한 사람의 수

if people>=n:
break;

# 검사할 수 있는 사람이 검사할 사람보다 많을 경우

if people >= n:
answer =mid;
right = mid - 1;

# 검사할 수 있는 사람이 검사할 사람보다 적을 경우

elif people<n:
left = mid + 1;

return answer;

로직은 다음과 같다.

  1. 최소시간(left)과 최대시간(right)을 정한다.
  2. 중간값인 (mid)시간에는 몇 명 검사할 수 있는지 검사한다.
    즉 mid를 기준으로 삼는다.
  3. n명 검사할 수 있을경우 right를 mid-1로, n명을 다 검사하지 못할 경우 left를 mid+1로 한다.

이는 mid값을 기준으로 각각의 경우에 해가 되는 right값은 mid의 왼쪽, 해가 되는 left값은 mid의 오른쪽에 위치하기 때문이다.

  1. right가 left보다 작아질 때 까지 반복한다.
    최대시간(right)이 최소시간(left)보다 작아졌다는 것은 이전 시행의 결과가 해라는 의미이다.

이진탐색

이진탐색은 기준을 정해놓고 범위를 줄여나가는 것이 핵심인 것 같다.

이 문제의 경우 n명을 검사해야 하므로 n명을 검사할 수 있는 범위를 구해 기준으로 하고 범위를 줄여나가는 방식으로 풀어야 했다.

정리

나와는 상성이 좋지 않지만 비슷한 유형의 문제를 풀다보면 풀 수 있을 것이라고 생각한다..!

React Image preload!

프론트에서 최적화를 하기 위해 이미지 자체를 webp등으로 최적화하는 방법이 있지만 다른 방법으로는 미리 로딩하고 캐시로 가져오는 preload 방식이 있다.

Preload

이미지를 미리 로딩하고 캐시로 가져오는 방식이다.
캐시는 브라우저차원에서 자동으로 하기 떄문에 이미지를 미리 받아오기만 하면 된다.

Preload 에는 두 가지 방식이 있다.

병렬(Parallel) 방식

자바스크립트의 경우 요청을 병렬로 처리하려고 하므로 for문을 돌면서 여러번 호출해주면 된다.

리액트의 경우 useEffect에서 첫 렌더링시에 호출하여 이미지를 미리 로드해두고 캐시해서 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
useEffect(() => {
function preloading(imageArray) {
imageArray.forEach((url) => {
const image = new Image();
image.src = url;
});
}

preloading(["1.png", "2.png", "3.png"]);
}, []);

특징

모든 요청을 한 번에 보내기 때문에 전체 사진을 불러오는데에 시간이 줄어든다. 하지만 전체 사진이 로드되기 전까지 기다려야한다.

modules

순차적(Sequential) 방식

순차적 방식은 재귀를 통해 구현한다. 이미지의 onload 이벤트에 다음 이미지를 parameter로 재귀호출하도록 하여 이미지가 우선순위를 갖고 캐싱될 수 있도록 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
useEffect(() => {
function preload(imageArray, index) {
index = index || 0;
if (imageArray && imageArray.length > index) {
const image = new Image();
image.onload = function () {
preload(imageArray, index + 1);
};
image.src = images[index];
}
}

preload(images);
}, []);

특징

병렬 방식에 비해 전체 작업시간은 늘어나지만 이미지들이 하나씩 로드되어 하나씩 확인할 수 있다.

modules

마무리

  • 병렬 : 빠르게 불러와야 할 때

  • 순차적 : 조금씩이라도 불러와야할 때, 추가로 슬라이더 등을 구현할 때 다음 이미지를 이 방식으로 미리 불러오면 좋을 것 같다.

ref

image preloading
이미지 프리로딩

github-pages를 이용한 배포하기

먼저 깃허브 repo에 push된 상태여야 한다.

설치

github-pages를 설치한다.

1
npm i gh-pages --save-dev

homepage 추가하기

package.json에 homepage를 추가한다.

주소는 http://{사용자 이름}.github.io/{저장소 이름}으로 한다.

1
2
3
//...
"homepage": "http://pshdev1030.github.io/wanted_pre_onboarding"
//...

script 추가하기

이어서 package.json에 해당 스크립트를 추가한다.

1
2
3
4
//...
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
//...

배포하기

터미널에서 배포하는 커맨드를 실행한다.

1
npm run deploy

확인하기

해당 저장소의 Settings->pages 에서 확인할 수 있다.
만약 접속이 안될 경우 뒤에 /index.html을 붙여 접속한다.

HTML label 태그와 input태그

<label> 태그는 사용자 인터페이스 항목의 설명을 나타내는 태그라고만 간략하게 알고있었다.

하지만 <label> 태그에는 내가 몰랐던 기능이 있었다.

<label>태그를 클릭하면 <input>태그가 클릭된다.

아래의 코드는 <label>태그 안에 <span><input>을 작성한 코드이다.

위에서 설명하였듯이 <label>을 클릭할 경우 <input>태그에도 이벤트가 전달된다.

때문에 여기에서 <span>을 클릭할 경우 이벤트 버블링에 의해서 <label>에도 이벤트가 전파되고 이로 인해 <input> 태그에도 이벤트가 전달된다.

즉 이벤트버블링을 명시적으로 막지 않는 한, <label>안의 요소를 클릭하면 <input>에 이벤트가 전달된다.

1
2
3
4
<label>
<input type="checkbox" />
<span></span>
</label>

useLayoutEffect

리액트의 Hooks 중에는 useLayoutEffect라는 Hook이 있다.
이 에 대해 정리해보았다.

Flow Diagram

Hook FLow Diagram에 따르면 Cleanup LayoutEffects는 브라우저가 페인트 작업을 하기 전에 실행된다.

hookflow

useLayoutEffect

위의 이미지를 보면 useEffect는 페인트 작업이 끝난 후 호출된다.

useLayoutEffect는 다음과 같이 작성하여 사용할 수 있다.

1
2
3
4
5
6
useLayoutEffect(() => {
// func
return () => {
// cleanup
};
}, [dependencies]);

사용 예시

useEffect 내부에서 상태값을 설정한다면
페인트를 마친 후에 상태값을 설정하며 리렌더링 되고 렌더링되지 않았던 값들이 렌더링되며 깜빡거리게 된다.

이럴 경우 useLayoutEffect를 호출하게 되면
페인트를 시작하기 전에 상태값을 설정하게 되고, 처음 렌더링시에 상태값이 이미 설정되어있어 깜빡거리지 않는다.

요약

useEffect 내부에서 상태값을 설정해야 한다면 useLayoutEffect를 사용하여 설정하자.( 물론 처음 화면을 렌더링하는데에 필요한 시간은 길어진다. 트레이드오프!)

ref

hook-flow

react hooks component에서 setInterval 사용의 문제점

useInterval이라는 커스텀 훅이 있다.

이 훅은 interval을 세팅하고, 컴포넌트가 언마운트 될 때 interval을 clera한다.
이렇게 함으로서 setInterval과 clearInterval 모두 컴포넌트 라이프 사이클에 종속된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React, { useState, useEffect, useRef } from "react";

function useInterval(callback, delay) {
const savedCallback = useRef();

// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);

// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}

setInterval + clearInerval의 단점

임피던스 불일치라는 용어를 쓴다.
리액트 프로그래밍 모델은 state를 기반으로 렌더링되고 기타 변수들을 관리한다.

하지만 setInterval은 일단 설정하고 나면 없애는 것 외에는 변경할 수 없다. 또한 react의 state 기반으로 동작하지도 않는다.

state나 props가 변하더라도 교체하기 전까지는 내부의 props와 state를 참조할 것이다.

ref의 사용

위에서 알아본 것 처럼 setInterval은 react의 동작과 다르게 동작한다.
이로 인해 발생하는 문제점은 다음과 같다.

  1. 첫 렌더에서 callback1을 가진 setInterval(callback1,delay)를 수행한다.
  2. 다음 렌더에서 새로운 props와 state를 거쳐서 만들어지는 callback2가 있다. 하지만 시간을 재설정하지 않고서는 callback을 대체할 수 없다.

이를 해결하려면 useRef를 사용하면 된다.

useRef는 hooks에서 렌더링과 관련없는 변수를 담는데에 이용되는 하나의 상자이다.

솔루션은 다음과 같다.

  1. setInterval(fn,delay)에서 함수가 savedCallback을 호출하게 만든다.
  2. 첫 렌더링에서 savedCallback을 callback1로 설정한다.
  3. 두 번째 렌더링에서 savedCallback을 callback2로 설정한다.

결과본은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, { useState, useEffect, useRef } from "react";

function useInterval(callback, delay) {
const savedCallback = useRef();

// callback에 변경사항이 있을 경우 가장 최근의 함수를 저장한다
// 최근의 state나 props의 변경사항을 반영한 callback이다.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);

// 타이머를 교체하거나 하는 작업 없이 제일 최근에 들어온 함수를 실행한다.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}

Interval 일시정지하기

위의 useInterval hook에선 null을 delay에 전달하는 것으로 interval을 일시 정지할 수 있다.
delay가 null이 아닐경우 타이머를 설정하기 떄문이다..!

1
2
3
4
5
6
7
8
9
const [delay, setDelay] = useState(1000);
const [isRunning, setIsRunning] = useState(true);

useInterval(
() => {
setCount(count + 1);
},
isRunning ? delay : null
);

ref

훅스 컴포넌트에서 setInterval 사용시의 문제점
Making setInterval Declarative with React Hooks

new 연선자와 생성자 함수

new연산자와 생성자 함수를 사용하여 유사한 객체 여러개를 쉽게 만들 수 있다.

생성자 함수

생성자 함수는 아래 두 관례를 따른다.

  1. 함수 이름의 첫 글자는 대문자로 시작한다.
  2. 반드시 new연산자를 붙여 실행한다.

생성자 함수는 다음과 같은 방식으로 동작한다.

1
2
3
4
5
6
7
8
function User(name) {
// this={}; (빈 객체가 암시적으로 만들어짐)
this.name = name;
this.isAdmin = false;
// return this;
}

let user = new User("sunghyeon");

styled-component+ts props 전달하기

typescript환경에서 styled-components에 props를 전달하려면 제네릭을 활용하면 된다.
나는 next.js를 사용중이라 @emotion/styled를 사용하였다.

예시

1
2
3
4
5
6
7
8
9
10
11
12
interface blockType {
blockColor: string;
sizeOfRowCol: number;
}

const Block = styled.div<blockType>`
background: ${(props) => props.blockColor};
width: ${(props) => (BlockWrapperSize - 10) / props.sizeOfRowCol}px;
height: ${(props) => (BlockWrapperSize - 10) / props.sizeOfRowCol}px;
margin: 2px;
box-sizing: border-box;
`;

실제 사용할 때는 컴포넌트에 props를 내려주고 접근하여 사용하면 된다.

1
2
3
4
5
6
7
<BlockWrapper>
{colorTable.map((block, idx) => (
<Block blockColor={block.color} sizeOfRowCol={2} key={idx}>
&nbsp;
</Block>
))}
</BlockWrapper>

2월 2주차 계획!

하고싶은 공부가 많다.
목록으로 작성하고 끝낼 때 마다 하나씩 없앨 계획이다.
다 완료하면 지우는 걸로..!

코테 하루 1문제(완료)

프로그래머스로 하는 스터디가 거의 다 끝나간다.
최근에 코테를 보면서 프로그래머스 문제들을 한 번씩은 다 풀어보았다.
진짜 손도 못댄 문제들이 몇 문제 남아있는데, 하루 두 시간정도 투자해서 꼭 풀어볼 예정이다..!

대표적으로 생각나는건 도둑질, 여행경로,,

useLayoutEffect vs useEffect(완료)

렌더링 이전 vs 렌더링 이후로 간략하게 알고 있다.

개념을 확실하게 정리하고 예제코드를 작성해볼 계획이다..!

프로그래머스 과제관 고양이(미완)

함수형 컴포넌트를 작성하여 풀었다.
근데 클래스형 컴포넌트를 쓰기를 피하고 싶어서 함수형으로 작성한 것이 크다 ㅠ
두 유형으로 한 번 씩 풀어보고 얻은 점을 정리해보아야겠다.

React에서 Interval 사용하기!(완료)

유명한 커스텀 훅인 useInterval이 있다.
이를 분석해보려고 한다.

원본

번역

좋은 아티클 정리하기(미완)

리액트 서버 컴포넌트
where를 사용하기
react as a ui runtime

넘블 챌린지 끝내기!(완료)

넘블 챌린지는 오늘 시작하였는데, 거의 끝나간다..!

선택) 원티드 프론트엔드 온보딩코스(완료)

과제가 프론트엔드에서 많이 쓰이는 토글이나 모달 등을 구현하는 것이던데, 한 번쯤 해볼 만 한 것 같다.

정리

적고보니 진짜 많다.
내 소중한 일주일 화이팅! 은서도 화이팅!

axios 요청 취소하기

axios는 요청을 취소할 수 있는 CancelToken을 제공한다.

1
2
3
4
5
6
cancelToken = axios.CancelToken.source();
// 토큰을 생성
axios.get(url, { cancelToken: cancelToken.token });
// 요청에 토큰을 넣어서 전송
cancelToken.cancel();
// 토큰의 cancel메서드를 이용하여 취소

토큰을 생성하여 axios의 config에 넣어주면 된다.
그 후 토큰의 cancel()메서드를 호출하면 된다.

아래의 코드는 리액트에서 사용한다고 가정하고 예시로 작성해본 코드이다..!

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
29
30
31
32
33
34
35
36
37
38
39
40
const Example = () => {
const ref = useRef();
const handleRequest = async () => {
try {
ref.current = axios.CancelToken.source();
const data = await axios.get(
url,
{
data:{/.../},
cancelToken: ref.current.token,
});
//...데이터 처리
}
catch (e) {
if (error.response) {
//서버에서 에러 메시지를 전달해주었을 경우
console.error(error.response.message);
}
else if (error.request) {
//서버에서 응답하지 않은 경우
console.error("request is failed!");
}
else if (axios.isCancel(error)) {
//요청을 취소한 경우
console.log("request is cancled!");
}
}
};
};

const handleRequestCancel=()=>{
res.current.cancel();
}

return(
<>
<button onClick={handleRequest}>요청</button>
<button onClick={handleRequestCancel}>취소</button>
</>
)

마지막 요청만 서버로 보내는 디바운싱과 함께 적용하면 요청을 효율적으로 관리할 수 있을 것 같다.

디바운싱을 적용하여 간단하게 작성해보았다.

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const Example = () => {
const ref = useRef();
const timer=useRef();
const handleRequest = () => {
if(timer.current){
clearTimeout(timer);
}
const newTimer=setTimeout(async ()=>{
try {
ref.current = axios.CancelToken.source();
const data = await axios.get(
url,
{
data:{/.../},
cancelToken: ref.current.token,
});
//...데이터 처리
}
catch (e) {
if (error.response) {
//서버에서 에러 메시지를 전달해주었을 경우
console.error(error.response.message);
}
else if (error.request) {
//서버에서 응답하지 않은 경우
console.error("request is failed!");
}
else if (axios.isCancel(error)) {
//요청을 취소한 경우
console.log("request is cancled!");
}
}
};
},1300)
timer.current=(newTimer)
};

const handleRequestCancel=()=>{
res.current.cancel();
}

return(
<>
<button onClick={handleRequest}>요청</button>
<button onClick={handleRequestCancel}>취소</button>
</>
)

다음에 진행할 프로젝트에 꼭 한 번 적용해보고 싶다!

ref

axios 러닝 가이드