0%

AboveMean

BelowMean

GreaterThan

LessThan

InsideRange

OutsideRange

InsideNthRange

OutsideNthRange

String

React Query 란?

리액트용 data-fetching 라이브러리로,

리액트 어플리케이션에서 서버의 상태를 fetching, caching, synchronizing, updating 한다.

react query는 다음과 같이 4가지 상태를 반환하는데, 반환 상태에 따라 렌더링을 다르게 처리하기 용이하단느 장점이 있다.

  1. isIdle : status === ‘idle’로, fresh/reset된 상태
  2. isLoading : status === ‘loading’으로, mutation이 진행 중인 상태
  3. isError : status === ‘error’로, mutation이 error를 발생시킨 상태
  4. isSuccess : status === ‘success’로, mutation이 성공하여 data 이용이 가능한 상태

설치하기

1
2
3
$ npm i react-query
# or
$ yarn add react-query

기본 예시

App.tsx

1
2
3
4
5
6
7
8
9
10
11
import { QueryClient, QueryClientProvider, useQuery } from 'react-query'

const queryClient = new QueryClient()

export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
  • 가장 상단 App.js 에서 최상단 컴포넌트로 QueryClientProvider 을 전달한다.

  • 해당 Provider context의 props로 queryClient 객체를 전달한다.

Example.tsx

1
2
3
4
5
6
function Example() {
const { isLoading, error, data } = useQuery('repoData', () =>
fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res =>
res.json()
)
)
  • useQuery
    • query key
    • fetch , axios 등을 이용한 CRUD 함수 를 전달한다.

useQuery 를 통해 isLoading , error , data 등을 받아 렌더링 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  if (isLoading) return 'Loading...'

if (error) return 'An error has occurred: ' + error.message

return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong>👀 {data.subscribers_count}</strong>{' '}
<strong>✨ {data.stargazers_count}</strong>{' '}
<strong>🍴 {data.forks_count}</strong>
</div>
)
}
  • isLoading 일 때는 로딩 렌더링
  • error 일 때는 에러 렌더링
  • 이 외, data 를 정상적으로 받아온 경우 컴포넌트를 알맞게 렌더링

useQuery

useQuery 가 반환하는 프로퍼티와 받는 매개변수는 다음과 같다.

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
48
49
50
51
52
53
54
55
56
const {
data,
dataUpdatedAt,
error,
errorUpdatedAt,
failureCount,
isError,
isFetched,
isFetchedAfterMount,
isFetching,
isIdle,
isLoading,
isLoadingError,
isPlaceholderData,
isPreviousData,
isRefetchError,
isStale,
isSuccess,
refetch,
remove,
status,
} = useQuery(queryKey, queryFn?, {
cacheTime,
enabled,
initialData,
initialDataUpdatedAt
isDataEqual,
keepPreviousData,
notifyOnChangeProps,
notifyOnChangePropsExclusions,
onError,
onSettled,
onSuccess,
queryKeyHashFn,
refetchInterval,
refetchIntervalInBackground,
refetchOnMount,
refetchOnReconnect,
refetchOnWindowFocus,
retry,
retryOnMount,
retryDelay,
select,
staleTime,
structuralSharing,
suspense,
useErrorBoundary,
})

// or using the object syntax

const result = useQuery({
queryKey,
queryFn,
enabled,
})

이를 사용한 예시는 다음과 같다.

먼저, useQuery에 전달할 getCategoryDetail api 함수를 작성해보자.

1
2
3
4
export const getCategoryList = async () => {
const data = await axios.get(`/api/category/`);
return data.data.result as CategoryList;
};

이제 해당 함수를 useQuery에 전달하여 데이터를 받아보자.

1
const { isLoading, error, data: categoryList } = useQuery('categoryList', getCategoryList);

여기서 categoryListquery key 이며, getCategoryList 를 통해 받아온 데이터는 isLoading , error , data 등을 담은 객체에 저장된다.

여기서 data: categoryList 로 데이터 변수명을 설정하여 컴포넌트를 알맞게 렌더링하면 된다!

useMutation

useMutation 이 반환하는 프로퍼티와 받는 매개변수는 다음과 같다.

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
const {
data,
error,
isError,
isIdle,
isLoading,
isPaused,
isSuccess,
mutate,
mutateAsync,
reset,
status,
} = useMutation(mutationFn, {
mutationKey,
onError,
onMutate,
onSettled,
onSuccess,
useErrorBoundary,
})

mutate(variables, {
onError,
onSettled,
onSuccess,
})

이제 useMutation을 사용해보자!

1
2
3
4
5
6
7
8
9
10
export const postItem = async (data: Item) => {
const data = await instance.post(
`/api/items`,
{
category_id: `${data.category_id}`,
item_name: `${data.candy_name}`,
},
);
return data.data.result as NewItem;
};

위와 같이 정의한 postItem api를 받아 mutation을 적용해보자.

1
2
3
4
5
6
const mutation = useMutation((data: PutBodyProps) => postItem(data), {
onSuccess: () => {
queryClient.invalidateQueries('items');
},
});

먼저 전달할 인자를 postItem 에 알맞게 전달하고, onSuccessqueryClient 를 통해 이전에 정의해놓은 query key 값을 전달하여 데이터를 mutate 시킨다.

Input

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다.

둘째 줄부터는 하나의 상자에 저장된 토마토들의 정보가 주어진다. 즉, 둘째 줄부터 N개의 줄에는 상자에 담긴 토마토의 정보가 주어진다. 하나의 줄에는 상자 가로줄에 들어있는 토마토의 상태가 M개의 정수로 주어진다. 정수 1은 익은 토마토, 정수 0은 익지 않은 토마토, 정수 -1은 토마토가 들어있지 않은 칸을 나타낸다.

토마토가 하나 이상 있는 경우만 입력으로 주어진다.

1
2
3
4
5
from collections import deque
import sys

M, N = map(int, sys.stdin.readline().split())
tomato = [list(map(int, sys.stdin.readline().split())) for _ in range(N)]

BFS

트리가 1단계씩 깊어질 때마다 기존의 조건을 동일하게 다시 체크해야 함 == BFS

사전 준비

  • 인접한 영역(가로,세로 => dir 로 방향 배열 만들어놓기)

    1
    dir = [[-1,0],[1,0],[0,-1],[0,1]]
  • queue 초기화 시 1 인 위치를 넣어놓는 배열

    1
    2
    3
    4
    5
    6
    ripen = deque()

    for i in range(N):
    for j in range(M):
    if tomato[i][j] == 1:
    ripen.append([i, j])
  • 이미 모두 1 인 경우를 체크할 isAlready boolean

    1
    isAlready = True

  • 체크하고 있는 칸이 범위 내이며 0 일 경우 의 값을 +1 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def bfs():
global isAlready

while ripen:
dx, dy = ripen.popleft()

for d in dir:
x = dx + d[0]
y = dy + d[1]

if 0 <= x < N and 0 <= y < M and tomato[x][y] == 0:
isAlready = False
tomato[x][y] = tomato[dx][dy] + 1
ripen.append([x, y])
  • 이미 모두 1 인 경우 isAlready boolean 값을 이용해 조건에 맞게 답을 출력한다.
  • 배열 전체가 True 가 아닌 경우를 체크해야 하므로 all 을 이용한다.
  • 이 외, bfs 의 결과 출력을 위해 배열 전체에서 max 값을 추출한다.
1
2
3
4
if not all(map(all,tomato)):
sys.stdout.write(str(-1))
elif isAlready: sys.stdout.write(str(0))
else: sys.stdout.write(str(max(map(max, tomato))-1))

위 모두에서 유용하게 쓰인 python 문법: 2차원 배열에서 최종값 하나 출력을 위해…

1
2
max(map(max, tomato))
all(map(all,tomato))

Output

여러분은 토마토가 모두 익을 때까지의 최소 날짜를 출력해야 한다. 만약, 저장될 때부터 모든 토마토가 익어있는 상태이면 0을 출력해야 하고, 토마토가 모두 익지는 못하는 상황이면 -1을 출력해야 한다.

1
2
3
4
5
bfs()
if not all(map(all,tomato)):
sys.stdout.write(str(-1))
elif isAlready: sys.stdout.write(str(0))
else: sys.stdout.write(str(max(map(max, tomato))-1))

Input

첫째 줄에 도시의 수 N이 주어진다. (2 ≤ N ≤ 10)

다음 N개의 줄에는 비용 행렬이 주어진다. 각 행렬의 성분은 1,000,000 이하의 양의 정수이며, 갈 수 없는 경우는 0이 주어진다. W[i][j]는 도시 i에서 j로 가기 위한 비용을 나타낸다.

항상 순회할 수 있는 경우만 입력으로 주어진다.

1
2
3
4
5
import sys
sys.setrecursionlimit(10 ** 6)

N = int(sys.stdin.readline())
cost = [list(map(int, sys.stdin.readline().split())) for _ in range(N)]

DFS

  • 시작점을 모두 다르게 해서 탐색해야 하므로 for 문으로 dfs 실행

    1
    2
    for now in range(N):
    dfs(now, now, 0, [now])
  • 시작점이 아니며, visited == False 이며, cost != 0 인 경우 dfs 실행

    • 실행 시 visited True 체크 했다가 모든 확인을 마치고 return 후 dfs를 빠져나오면 해당 노드를 False 체크하여 다음 경우의 수를 검사하도록 함
    1
    2
    3
    4
    5
    for idx in range(N):
    if cost[next][idx] != 0 and idx not in visited:
    visited.append(idx)
    dfs(start, idx, total + cost[next][idx], visited)
    visited.pop()
  • visited 의 길이가 N 인 경우 모든 노드를 방문한 것이므로 start 지점으로 돌아와야 한다. 이 때 cost != 0 이라면 min 을 업데이트한다.

    1
    2
    3
    4
    if len(visited) == N:
    if cost[next][start] != 0:
    minCost = min(minCost, total + cost[next][start])
    return
  • 가장 중요한 것은: 현재 total 값이 이미 minCost 를 초과한 경우, return 을 통해 빠져나와야 시간 초과가 뜨지 않는다!!!

Output

첫째 줄에 외판원의 순회에 필요한 최소 비용을 출력한다.

1
2
3
4
5
6
  if len(visited) == N:
if cost[next][start] != 0:
minCost = min(minCost, total + cost[next][start])
return

sys.stdout.write(str(minCost))

Git

VCS(Version Control System)

  • 오픈 소스 프로젝트에 contribute 가능

Index/ Staging Area

![Git%20Flow%20db159d7b7a334844bbdc9cd55df0c6d2/Untitled.png](/Users/JungHyunLah/Downloads/Export-7c14f1ac-12d4-4546-bf05-ffd2280e2b78/Git Flow db159d7b7a334844bbdc9cd55df0c6d2/Untitled.png)

Git Flow

feature > develop > release > hotfix > master

  • master
    • production에 사용되는 브랜치
  • feature
    • 브랜치 나오는 곳 : develop
    • 브랜치가 들어가는 곳 : develop
    • 이름 지정 : masterdeveloprelease-*hotfix-*를 제외한 어떤 것이든 가능.
    • 새로운 기능을 추가하는 브랜치
    • origin 에는 반영하지 않고 개발자의 repo 에만 존재
    • 여기서 merge를 할 때, --no-ff 옵션을 이용하여 브런치에서 머지가 되었음을 git 기록에 남겨두도록 함
  • release
    • 브랜치 나오는 곳 : develop
    • 브랜치가 들어가는 곳 : developmaster
    • 이름 지정 : release-*
    • 새로운 production release를 위한 브랜치
    • 현재까지의 기능을 develop 에서 release 로 따오고, develop 에는 다음번 릴리즈를 준비한다. 그리고 다가오는 릴리즈를 위해서 release 브랜치에서 버그 픽스에 대한 부분만 커밋하고, 최종적으로 master--no-ff 한다.
  • hotfix
    • 브랜치 나오는 곳 : master
    • 브랜치가 들어가는 곳 : developmaster
    • 이름 지정 : hotfix-*
    • production에서 발생한 문제를 해결하는 브랜치
    • 만약 release 브런치가 존재한다면, release 브런치에 hotfix 브런치를 머지하여 릴리즈 될 때 반영이 될 수 있도록 한다.

Input

첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다.

둘째 줄에는 네트워크 상에서 직접 연결되어 있는 컴퓨터 쌍의 수가 주어진다.

이어서 그 수만큼 한 줄에 한 쌍씩 네트워크 상에서 직접 연결되어 있는 컴퓨터의 번호 쌍이 주어진다.

1
2
3
4
5
import sys

N = int(sys.stdin.readline())
pair = int(sys.stdin.readline())
connected = [[] for _ in range(N+1)]

연결되어 있는 경우

  • connected 배열에 연결된 indexappend 한다!
1
2
3
4
for _ in range(pair):
a, b = map(int, sys.stdin.readline().split())
connected[a].append(b)
connected[b].append(a)

DFS

  • 전달된 인자를 visited = True 설정하고,
  • 해당 인자를 connected의 index로 설정하여 연결된 컴퓨터들을 찾는다.
  • 이 때, 연결된 컴퓨터가 not visited 일 경우 재귀 호출
1
2
3
4
5
6
7
8
9
10
visited = [False]*(N+1)

def dfs(now):
global count
count += 1
visited[now] = True

for next in connected[now]:
if not visited[next]:
dfs(next)
  • 1번이 걸린 경우 이어서 걸릴 애들을 세는 것이므로 count = -1 로 초기화하고 세기 시작한다.
1
count = -1

Output

1번 컴퓨터가 웜 바이러스에 걸렸을 때, 1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수를 첫째 줄에 출력한다.

1
2
dfs(1)
sys.stdout.write(str(count))

Input

Default

  • 런타임에러 방지를 위해 sys.stdinsys.stdout 을 이용한다.

첫 번째 줄에 수빈이가 있는 위치 N과 동생이 있는 위치 K가 주어진다. N과 K는 정수이다.

1
2
3
import sys

N, K = map(int, sys.stdin.readline().split())

BFS

  • 이진 탐색 그래프에서 가장 얕은 깊이를 출력하면 됨 == BFS
1
2
3
4
5
def bfs():
queue = [N]

while queue:
now = queue.pop(0)
  • now == K 일 때, 해당 깊이를 출력하면 됨
1
2
3
if now == K:
sys.stdout.write(str(cnt[now]))
return
  • 그래프를 그리며 경우의 수를 파악하였지만, 모든 경우에 대하여 bfs 하는 것을 생각하지 못함
    • now-1, now+1, 2*now
  • 주어진 범위에 따라 범위 내의 값인지 파악하고
  • 한 단계 더 깊어질 경우 깊이 == 현재의 높이+1 이므로, cnt += 1 을 시행한다.
  • 이를 위해 0으로 초기화된 범위 만큼의 배열을 생성한다.
1
cnt = [0]*100001
1
2
3
4
for next in (now-1, now+1, 2*now):
if 0 <= next <= 100000 and not cnt[next]:
queue.append(next)
cnt[next] = cnt[now]+1

Output

수빈이가 동생을 찾는 가장 빠른 시간을 출력한다.

1
2
3
if now == K:
sys.stdout.write(str(cnt[now]))
return

Input

python을 사용할 때 시간 초과 를 방지하기 위해, sys 를 이용하여

sys.stdin.readline() 혹은 sys.stdout.write() 를 사용한다!

sys.stdout.write()argsstring 이어야 하므로, str() 를 사용할 것!

첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2)

둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주어진다.

1
2
3
4
5
6
7
N, M = map(int, sys.stdin.readline().split())
adj = [[] for _ in range(N+1)]

for _ in range(M):
u, v = map(int, sys.stdin.readline().split())
adj[u].append(v)
adj[v].append(u)

DFS

  • 일단 visited = True 로 체크하고,
  • adj 배열의 요소를 전부 돌면서 아직 visited 하지 않은 경우 같은 과정을 반복하며 visited = True 로 체크한다. == DFS
1
2
3
4
5
6
def dfs(u):
visited[u] = True

for v in adj[u]:
if not visited[v]:
dfs(v)

Default

  • 재귀함수 문제에서는 항상 sys.setrecursionlimit() 를 설정하자.
1
2
3
4
import sys
sys.setrecursionlimit(10000)

visited = [False]*(N+1)
1
2
3
4
5
6
count = 0

for i in range(1, N+1):
if not visited[i]:
dfs(i)
count += 1

Wrong Answer

처음 시도했던 방법은 아래와 같은데, 시간 초과 가 뜸.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
graph = [list(map(int, input().split())) for _ in range(M)]
visited = [False]*(N+1)

def dfs(u):
visited[u] = True

for i in range(M):
if graph[i][0] == u:
v = graph[i][1]
if not visited[v]:
dfs(v)

count = 0

for i in range(1, N+1):
if not visited[i]:
dfs(i)
count += 1
  • 다른 부분은 input() 과,

  • 입력을 근접한 노드를 담는 adj 대신 그대로 저장하는 2차원 배열 graph 을 사용한 것 뿐인데

sys 을 이용해도 위 코드가 시간 초과 가 뜨는 이유는 무엇일까?

; 이유는 위 코드는 근접한 노드만을 하나씩 돌며 dfs를 실행하는 것이 아니라,

  • 주어진 조건을 모두 다시 돌며 index == 0 의 값이 해당 값인지 체크하고,
  • 같을 때 dfs를 실행해야 하므로 N, M가 클 때는 배열을 전부 다시 탐색하는 것이 근접한 노드만 검사할 때보다 훨씬 시간이 많이 필요하기 때문.

Output

첫째 줄에 연결 요소의 개수를 출력한다.

1
sys.stdout.write(str(count))

multiplication 과 for문의 차이

  • multiplication
1
2
3
4
5
6
>>> a =  [[1]*8]*8
>>> a
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]]
  • for
1
2
3
4
5
6
>>> A =  [[1 for i in range(8)] for j in range(8)]
>>> A
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]]

In your first version, you’re creating a list containing the number 1 and by multiplying it 8 times create a list containing 8 1s, and using that list 8 times to create a.

So when you change anything in the first version, you’ll see that change everywhere else. Your problem being that you’re reusing the same instance, something that doesn’t happen in the second version.

Input

1
2
N = int(input())
latitude = [list(map(int, input().split())) for _ in range(N)]

DFS

recursion limit 설정하기

이 문제를 파이썬에서 풀기 위해서 무조건 sys.setrecursionlimit()을 추가해줘야 한다. 이 코드를 삽입하지 않으면 런타임 에러가 발생한다. 공식 도큐먼트에 의하면 해당 코드는 파이썬 인터프리터 stack에 최대 깊이를 지정하는 것이다. 무한대의 recursion이 발생해서 overflow가 발생하는 것을 방지하기 위함이다. 이 문제에서는 그 만큼 깊이 있게 recursion으로 stack이 쌓이기 때문에 어느 정도가 진행되면 제한을 둬야 하는 것이다.

ref > https://dojinkimm.github.io/problem_solving/2019/11/15/boj-2468-safezone.html

  • recursion 을 사용할 때 runtime error 가 발생한다면 sys.setrecursionlimit() 을 설정하자!!
1
2
import sys
sys.setrecursionlimit(100000)

direction 배열 선언해놓기

  • 배열로 x, y 한번에 만들어도 좋고
  • x, y 따로 선언해서 같은 index로 접근해도 좋다.
1
dir = [[-1,0],[1,0],[0,-1],[0,1]]

visited 배열 생성하기

1
visited = [[0]*N for _ in range(N)]

조건

  • 가능한 높이를 조건에 따라 세팅해놓고,
  • 매번 비교하며 max 값을 구해야 하므로 기본 변수들을 초기화시킨다.
1
2
3
4
maxHeight = 100
for height in range(100):
count = 0
visited = [[0]*N for _ in range(N)]
  • 영역을 연속적으로 구하는 것이므로, 동일한 조건을 동일하게 반복하며 실행한다 === DFS
1
2
3
4
5
6
7
8
9
10
def dfs(dy, dx):
visited[dy][dx] = 1

for d in dir:
y = dy + d[0]
x = dx + d[1]

if 0 <= x < N and 0 <= y < N:
if visited[y][x] == 0 and latitude[y][x] >= height:
dfs(y, x)
  • 2차원 배열을 모두 돌며 조건에 맞는 경우 DFS
    1. 방문하지 않음
    2. 고도가 기준보다 높거나 같음 (잠기지 않음)
  • 이후 빠져나온 경우는 영역이 정해졌다는 의미이므로 count++ 를 실행한다.
1
2
3
4
5
6
for i in range(N): 
for j in range(N):
if visited[i][j] == 0 and latitude[i][j] >= height:
# visited 체크용 (세트 만들기)
dfs(i, j)
count += 1
  • 배열을 모두 검사하면 해당 height 에 대한 검사가 완료되었다는 뜻이므로 max 값을 구한다.
1
maxCount = max(maxCount, count)

Output

첫째 줄에 장마철에 물에 잠기지 않는 안전한 영역의 최대 개수를 출력한다.

1
print(maxCount)