diff --git a/404.html b/404.html index 4868e86..91e8c9d 100644 --- a/404.html +++ b/404.html @@ -1 +1 @@ -404: This page could not be found

404

This page could not be found.

\ No newline at end of file +404: This page could not be found

404

This page could not be found.

\ No newline at end of file diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/Algorithm.json b/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/Algorithm.json deleted file mode 100644 index 07d098d..0000000 --- a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/Algorithm.json +++ /dev/null @@ -1 +0,0 @@ -{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs"],"currentTag":"Algorithm","posts":[{"slug":"posts/Algorithm/[PCCP 기출문제]퍼즐게임 챌린지","title":"[PCCP 기출문제] 퍼즐게임 챌린지","date":"2024-10-31T12:05:24.000Z","image":"index.jpg","summary":"프로그래머스 [PCCP 기출문제] level 2 퍼즐게임 챌린지 문제 풀이 입니다.","tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색"],"content":"# [PCCP 기출문제] 퍼즐게임 챌린지\r\n\r\n## 문제 요약\r\nhttps://school.programmers.co.kr/learn/courses/30/lessons/340212\r\n\r\n숙련도에 따라 퍼즐을 풀 때, \r\n- 각 퍼즐에서 난이도가 숙련도 보다 높다면 **난이도 - 숙련도 만큼 퍼즐이 틀리고, 틀린횟수 * (이전시간 + 현재시간)만큼의 시간이 걸린다.**\r\n- 각 퍼즐에서 난이도가 숙련도 보다 낮거나 같다면 **현재시간만** 투자하면 된다.\r\n\r\n이렇게 모든 퍼즐을 푼 시간의 합이 주어진 limit를 넘지 않도록 하는 숙련도(level)의 최솟값을 구하여야한다. \r\n\r\n(자세한 문제의 내용은 링크를 참고바랍니다.)\r\n\r\n## 문제 접근\r\n레벨의 최솟값을 구하여야 하기에, 처음에는 diffs 난이도 배열에서 최댓값부터 시작하여 1씩 줄어들도록 푸는 브루트포스를 생각했다.\r\n\r\n하지만, 제한사항이 1<=diffs[i]<=100000로 범위가 상당히 크고, diffs의 길이도 1 ≤ diffs의 길이 = times의 길이 = n ≤ 300,000로 제한사항이 주어졌기에\r\n\r\n브루트포스 기법은 무조건 시간초과가 날 것이므로 아닌 다른 방식을 생각해내야한다. \r\n\r\n결국 level의 값을 효율적이고 시간이 적게 들 수 있게 탐색해내야 하므로, O(nlogn)의 시간 복잡도를 가지는 **이분탐색으로 level의 최솟값을 구하면 된다.**\r\n\r\n## 풀이\r\n최대 난이도 이상의 값을 탐색하는 것은 의미가 없으므로, \r\n난이도의 최댓값을 구하여, max_diff로 놓은다음 이를 초기에 r로 설정한다.\r\nl은 난이도의 최솟값인 1로 두고,\r\n\r\n1~max_diff 범위 안에서 레벨의 이분탐색을 진행한다.\r\n\r\n현재 레벨을 변수 `level`로 둘 때,\r\n\r\n각 `level`에서 나올 수 있는 경우는 퍼즐을 푸는 시간의 합이 `limit`보다 **높을 때**와 **낮거나 같을 때**로 나눌 수 있다.\r\n- 시간의 합이 `limit`보다 높을 때: 현재 레벨이 낮기에 그런 것 이므로, 더 높은 레벨을 탐색하기 위해 \r\n`l = level+1` 로 둔다.\r\n\r\n- 시간의 합이 `limit` 보다 낮거나 같을 때: 현재 레벨이 충분히 높기에, 우리는 레벨의 최솟값을 찾아내야 하므로, \r\n`r = level-1`로 두고, 현재 레벨이 답이 될 수도 있기에 `answer = level` 도 추가해야 한다.\r\n\r\n이를 전체 코드로 구현하면 다음과 같다.\r\n\r\n## 전체 코드\r\n```js\r\n def solution(diffs, times, limit):\r\n max_diff = max(diffs)\r\n l = 1\r\n r = max_diff;\r\n answer = max_diff;\r\n while l 0:\r\n time += w_count*(times[i-1] + times[i]) + times[i];\r\n else:\r\n time += times[i];\r\n if time>limit:\r\n l = level+1;\r\n else:\r\n r = level;\r\n answer = level;\r\n return answer;\r\n ```\r\n\r\n## 느낀 점\r\n한 동안 알고리즘 문제를 안풀었더니 뇌가 굳은게 아닌가 싶다. 그리고 문제 설명 자체가 길고 복잡하면 살짝 길을 잃는 습관이 있는데, 이번 문제에서도 이해해보면 그렇게 어렵지 않은 것 같음에도 불구하고 그런 습관이 나타난 것 같다. \r\n\r\n전에 백준에서 풀었던 이분탐색문제랑 거의 흡사했음에도 이분탐색을 빨리 떠올리지 못한게 아쉽다."},{"slug":"posts/Algorithm/[백준 gold 5] 빌런 호석","title":"[백준 gold 5] 빌런 호석","date":"2024-08-09T17:05:24.000Z","image":"index.png","summary":"dfs을 활용한 프로그래머스 [level 3] 징검다리 건너기 문제 풀이 입니다.","tags":["Algorithm","백준","gold","dfs"],"content":"# [gold 3] 빌런 호석\r\n\r\n## 문제 설명\r\n\r\n치르보기 빌딩은 11층부터 NN층까지 이용이 가능한 엘리베이터가 있다. 엘리베이터의 층수를 보여주는 디스플레이에는 KK 자리의 수가 보인다. 수는 00으로 시작할 수도 있다. 00부터 99까지의 각 숫자가 디스플레이에 보이는 방식은 아래와 같다. 각 숫자는 7개의 표시등 중의 일부에 불이 들어오면서 표현된다.\r\n\r\n![](1.png)\r\n\r\n예를 들어 K=4K=4인 경우에 16801680층과 501501층은 아래와 같이 보인다.\r\n\r\n![](2.png)\r\n\r\n\r\n\r\n빌런 호석은 치르보기 빌딩의 엘리베이터 디스플레이의 LED 중에서 최소 11개, 최대 PP개를 반전시킬 계획을 세우고 있다. 반전이란 켜진 부분은 끄고, 꺼진 부분은 켜는 것을 의미한다. 예를 들어 숫자 11을 22로 바꾸려면 총 5개의 LED를 반전시켜야 한다. 또한 반전 이후에 디스플레이에 올바른 수가 보여지면서 11 이상 NN 이하가 되도록 바꿔서 사람들을 헷갈리게 할 예정이다. 치르보기를 사랑하는 모임의 회원인 당신은 호석 빌런의 행동을 미리 파악해서 혼쭐을 내주고자 한다. 현재 엘리베이터가 실제로는 XX층에 멈춰있을 때, 호석이가 반전시킬 LED를 고를 수 있는 경우의 수를 계산해보자.\r\n\r\n### 입력\r\n\r\n N,K,P,XN,K,P,X 가 공백으로 구분되어 첫째 줄에 주어진다.\r\n\r\n### 출력\r\n\r\n호석 빌런이 엘리베이터 LED를 올바르게 반전시킬 수 있는 경우의 수를 계산해보자.\r\n\r\n## 접근 방법\r\n\r\n숫자에 따른 LED를 먼저 구현하기 위해, \r\n\r\n그림과 같이, 각 LED의 위치에 번호를 매겨, 배열안에 그 번호가 있으면 불이 켜져있는 것으로, 없다면 불이 꺼져 있는 것으로 표현하였다.\r\n\"이미지\r\n\r\n\r\n그림처럼 0을 표현하면 3번자리를 제외한 모든 번호가 켜져있으므로, `[0,1,2,4,5,6]`으로 표현 할 수 있다.\r\n\r\n각 번호를 이와 같이 바꾸어 numbers 배열에 저장하면, 이와 같다.\r\n```python\r\nnumbers = [[0,1,2,4,5,6],[2,5],[0,2,3,4,6],[0,2,3,5,6],[1,2,3,5],[0,1,3,5,6],[0,1,3,4,5,6],[0,2,5],[0,1,2,3,4,5,6],[0,1,2,3,5,6]];\r\n```\r\n이후 만일 현재 번호와 비교할 번호의 LED를 비교하기 위해 서로의 차집합을 더해서 두 번호가 서로를 비교했을 때, 가지고 있는 혹은 없는 번호의 개수를 구한다.\r\n```python\r\ncur_count = len(list(cur.difference(compare))) + len(list(compare.difference(cur)));\r\n```\r\n이와 같은 방식으로 반전시킬 LED의 개수를 구하고, dfs를 활용하여 x의 모든 자릿수를 순회하여 p보다 작거나 같게 LED를 반전하면서, n보다 번호가 작거나 같다면 result를 1 증가 시킨다.\r\n\r\n전체코드는 다음과 같다.\r\n```python\r\nimport sys;\r\ninput = sys.stdin.readline;\r\n\r\nn,k,p,x = map(int, input().split());\r\nresult = 0;\r\nnumbers = [[0,1,2,4,5,6],[2,5],[0,2,3,4,6],[0,2,3,5,6],[1,2,3,5],[0,1,3,5,6],[0,1,3,4,5,6],[0,2,5],[0,1,2,3,4,5,6],[0,1,2,3,5,6]];\r\n\r\nx = list(str(x));\r\nif len(x) != k:\r\n\t# k 보다 자릿수 x가 자릿수가 작다면 작은 만큼 앞에 0을 채워주어야 한다.\r\n for _ in range(k-len(x)):\r\n x.insert(0,'0');\r\n \r\ndef dfs(index,count,st):\r\n global result;\r\n if count>p:\r\n return;\r\n if index == k:\r\n if 0\r\n \r\nrectangle\r\ncharacterX\r\ncharacterY\r\nitemX\r\nitemY\r\nresult\r\n\r\n\r\n \r\n[[1,1,7,4],[3,2,5,5],[4,3,6,9],[2,6,8,8]]\r\n1\r\n3\r\n7\r\n8\r\n17\r\n\r\n\r\n[[1,1,8,4],[2,2,4,9],[3,6,9,8],[6,3,7,7]]\r\n9\r\n7\r\n6\r\n1\r\n11\r\n\r\n\r\n[[1,1,5,7]]\r\n1\r\n1\r\n4\r\n7\r\n9\r\n\r\n\r\n[[2,1,7,5],[6,4,10,10]]\r\n3\r\n1\r\n7\r\n10\r\n15\r\n\r\n\r\n[[2,2,5,5],[1,3,6,4],[3,1,4,6]]\r\n1\r\n4\r\n6\r\n3\r\n10\r\n\r\n\r\n \r\n\r\n##### 입출력 예 설명\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88-%EC%84%A4%EB%AA%85)\r\n\r\n입출력 예 #1\r\n\r\n![rect_5.png](6.png)\r\n\r\n캐릭터 위치는 (1, 3)이며, 아이템 위치는 (7, 8)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n입출력 예 #2\r\n\r\n![rect_7.png](7.png)\r\n\r\n캐릭터 위치는 (9, 7)이며, 아이템 위치는 (6, 1)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n입출력 예 #3\r\n\r\n[![rect_8.png](8.png)]\r\n\r\n캐릭터 위치는 (1, 1)이며, 아이템 위치는 (4, 7)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n## 구현 방법\r\n### 전체 직사각형들을 합친 것의 바깥쪽 테두리만 돌게하려면 어떻게 하는가?\r\n- rectangle을 순회하면서 범위 중 직사각형 내부는 0으로 테두리는 1로 board값을 갱신한다.\r\n- 이 때, 만일 `다른 직사각형의 내부안에 현재 직사각형의 테두리가 있지 않을 경우`에만 board를 1로 갱신한다. \r\n### 예외 사항\r\n![exception.png](exception.png)\r\n좌표가 인접한 경우 위와 같이 의도하지 않은 경우를 초래할 수도 있다.\r\n\r\n따라서 이를 해결하기 위해 모든 좌표의 값을 **2배**씩 해주어야 한다.\r\n\r\n이를 모두 반영하여 board 테이블을 만들면,\r\n\r\n```python\r\nboard = [[-1 for _ in range(102)] for _ in range(102)];\r\nfor r in rectangle:\r\n\t# map 객체를 통해 모든 좌표값에 2배\r\n\tx1,y1,x2,y2 = map(lambda x: x*2,r);\r\n for i in range(x1,x2+1):\r\n for j in range(y1,y2+1):\r\n\t # 직사각형 내부의 경우 0으로 개신\r\n if x1\r\n \r\nstones\r\nk\r\nresult\r\n\r\n\r\n \r\n[2, 4, 5, 3, 2, 1, 4, 2, 5, 1]\r\n3\r\n3\r\n\r\n\r\n \r\n\r\n##### **입출력 예에 대한 설명**\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/64062.%E2%80%85%EC%A7%95%EA%B2%80%EB%8B%A4%EB%A6%AC%E2%80%85%EA%B1%B4%EB%84%88%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88%EC%97%90-%EB%8C%80%ED%95%9C-%EC%84%A4%EB%AA%85)\r\n\r\n----------\r\n\r\n**입출력 예 #1**\r\n\r\n첫 번째 친구는 다음과 같이 징검다리를 건널 수 있습니다. \r\n![1.png](1.png)\r\n\r\n첫 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n두 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다. \r\n![2.png](2.png)\r\n\r\n두 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n세 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다. \r\n![3.png](3.png)\r\n\r\n세 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n네 번째 친구가 징검다리를 건너려면, 세 번째 디딤돌에서 일곱 번째 디딤돌로 네 칸을 건너뛰어야 합니다. 하지만 k = 3 이므로 건너뛸 수 없습니다. \r\n![4.png](4.png)\r\n\r\n따라서 최대 3명이 디딤돌을 모두 건널 수 있습니다.\r\n\r\n## 접근 방법\r\n브루트 포스로 1명씩 건너기에는, stones 배열의 크기가 최대 200,000이고, stones 배열의 각 원소들의 값 역시 최대 200,000,000이기 때문에, 시간 초과가 날 확률이 높다고 생각했고(사실 그렇게 쉬우면 lv3 일리가 없지), 아니나 다를까 시간초과가 났다. \r\n\r\n고민하다가 도저히 해결책이 안나와서 찾아보니 `이분탐색`을 사용하여 풀 수 있었다.\r\n\r\n- 건널 수 있는 인원의 최소는 1명, 최대는 max(stones)명이다. 모든 돌들의 합이 같거나, 다르다 하더라도 건널수 없는 곳이 k이하면 건널 수 있기 때문이다. \r\n- 따라서 start = 1 end = max(stones)로 이분탐색을 시작할 수 있다.\r\n\r\nmid 값만큼의 사람이 건널 수 있는지 확인하기 위한 check함수는 다음과 같다.\r\n```python\r\ndef check(stones, mid,k):\r\n cnt = 0;\r\n for stone in stones:\r\n\t # 밟을 수 없으므로 건너뛰어야하는 횟수를 늘린다.\r\n if (stone - mid) <= 0:\r\n cnt += 1;\r\n # 밟을 수 있으므로 최대 건너뛰는 횟수를 초기화시킨다.\r\n else:\r\n cnt = 0;\r\n # 건너뛰는 횟수가 k보다 크거나 같으면 안되므로 False 리턴 \r\n if cnt>=k:\r\n return False;\r\n return True;\r\n```\r\n건널 수 있다면 mid보다 큰 범위에서 값을 찾아야하므로 start를 mid+1로 갱신하고, 반대라면, mid보다 작은 범위에서 값을 찾아야하므로 end를 mid-1로 갱신한다.\r\n \r\ncheck 시간 복잡도는 O(N)이고 이분탐색을 했을 때 O(logN)의 시간복잡도가 나오므로, 총 O(NlogN)의 시간 복잡도가 나오므로, 시간초과가 나지 않는다.\r\n\r\n전체 코드는 다음과 같다.\r\n```python\r\ndef check(stones, mid,k):\r\n cnt = 0;\r\n for stone in stones:\r\n if (stone - mid) <= 0:\r\n cnt += 1;\r\n else:\r\n cnt = 0;\r\n if cnt>=k:\r\n return False;\r\n return True;\r\n\r\ndef solution(stones, k):\r\n start,end = 1,max(stones);\r\n answer = 0;\r\n while start<=end:\r\n mid = (start + end) // 2;\r\n if check(stones, mid,k):\r\n start = mid+1;\r\n else:\r\n answer = mid;\r\n end = mid-1; \r\n return answer;\r\n```\r\n\r\n## 느낀 점\r\n이분탐색 문제를 많이는 아니더라도 어느정도는 풀어봤다고 생각했는데, 이 문제에서 이분탐색으로 푸는 아이디어를 떠올리지 못해서 아쉽다. \r\n\r\n또한 처음에 위에 코드의 6번째줄에서 else문을 쓰지않고, 이러한 코드로 제출하여서 시간초과가 났다.\r\n```python\r\nif (stone - mid) <= 0:\r\n\tcnt += 1;\r\nif (stone - mid)>0:\r\n\tcnt = 0;\r\n```\r\nㅎㅎ.. 사실 위와 같은 경우에서 if else문을 쓰는 것이 기본인데, 그래도 시간초과가 날 수 있는 직접적인 원인까지는 되지 않을 거라고 안일하게 생각해서 코드를 짠 게 화근이었다. 기본을 지키자!"}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/bfs.json b/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/bfs.json deleted file mode 100644 index 8291c48..0000000 --- a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/bfs.json +++ /dev/null @@ -1 +0,0 @@ -{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs","all"],"currentTag":"bfs","posts":[{"slug":"posts/Algorithm/[프로그래머스lv3]아이템 줍기","title":"[프로그래머스 level 3] 아이템 줍기","date":"2024-07-12T16:34:24.000Z","image":"index.png","summary":"bfs를 활용한 프로그래머스 [level 3] 아이템 줍기 문제 풀이 입니다.","tags":["Algorithm","프로그래머스","lv3","bfs"],"content":"# [level 3] 아이템 줍기 (BFS/DFS)\r\n## 문제 설명\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EB%AC%B8%EC%A0%9C-%EC%84%A4%EB%AA%85)\r\n\r\n다음과 같은 다각형 모양 지형에서 캐릭터가 아이템을 줍기 위해 이동하려 합니다.\r\n\r\n![1.png](1.png)\r\n\r\n지형은 각 변이 x축, y축과 평행한 직사각형이 겹쳐진 형태로 표현하며, 캐릭터는 이 다각형의 둘레(굵은 선)를 따라서 이동합니다.\r\n\r\n만약 직사각형을 겹친 후 다음과 같이 중앙에 빈 공간이 생기는 경우, 다각형의 가장 바깥쪽 테두리가 캐릭터의 이동 경로가 됩니다.\r\n\r\n![2.png](2.png)\r\n\r\n단, 서로 다른 두 직사각형의 x축 좌표 또는 y축 좌표가 같은 경우는 없습니다.\r\n\r\n![rect_3.png](3.png)\r\n\r\n즉, 위 그림처럼 서로 다른 두 직사각형이 꼭짓점에서 만나거나, 변이 겹치는 경우 등은 없습니다.\r\n\r\n다음 그림과 같이 지형이 2개 이상으로 분리된 경우도 없습니다.\r\n\r\n![rect_4.png](4.png)\r\n\r\n한 직사각형이 다른 직사각형 안에 완전히 포함되는 경우 또한 없습니다.\r\n\r\n![rect_5.png](5.png)\r\n\r\n지형을 나타내는 직사각형이 담긴 2차원 배열 rectangle, 초기 캐릭터의 위치 characterX, characterY, 아이템의 위치 itemX, itemY가 solution 함수의 매개변수로 주어질 때, 캐릭터가 아이템을 줍기 위해 이동해야 하는 가장 짧은 거리를 return 하도록 solution 함수를 완성해주세요.\r\n\r\n##### 제한사항\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EC%A0%9C%ED%95%9C%EC%82%AC%ED%95%AD)\r\n\r\n- rectangle의 세로(행) 길이는 1 이상 4 이하입니다.\r\n- rectangle의 원소는 각 직사각형의 [좌측 하단 x, 좌측 하단 y, 우측 상단 x, 우측 상단 y] 좌표 형태입니다.\r\n - 직사각형을 나타내는 모든 좌표값은 1 이상 50 이하인 자연수입니다.\r\n - 서로 다른 두 직사각형의 x축 좌표, 혹은 y축 좌표가 같은 경우는 없습니다.\r\n - 문제에 주어진 조건에 맞는 직사각형만 입력으로 주어집니다.\r\n- charcterX, charcterY는 1 이상 50 이하인 자연수입니다.\r\n - 지형을 나타내는 다각형 테두리 위의 한 점이 주어집니다.\r\n- itemX, itemY는 1 이상 50 이하인 자연수입니다.\r\n - 지형을 나타내는 다각형 테두리 위의 한 점이 주어집니다.\r\n- 캐릭터와 아이템의 처음 위치가 같은 경우는 없습니다.\r\n\r\n----------\r\n\r\n- 전체 배점의 50%는 직사각형이 1개인 경우입니다. \r\n \r\n- 전체 배점의 25%는 직사각형이 2개인 경우입니다. \r\n \r\n- 전체 배점의 25%는 직사각형이 3개 또는 4개인 경우입니다. \r\n \r\n\r\n----------\r\n\r\n##### 입출력 예\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88)\r\n\r\n\r\n\r\n \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n
rectanglecharacterXcharacterYitemXitemYresult
[[1,1,7,4],[3,2,5,5],[4,3,6,9],[2,6,8,8]]137817
[[1,1,8,4],[2,2,4,9],[3,6,9,8],[6,3,7,7]]976111
[[1,1,5,7]]11479
[[2,1,7,5],[6,4,10,10]]3171015
[[2,2,5,5],[1,3,6,4],[3,1,4,6]]146310
\r\n\r\n##### 입출력 예 설명\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88-%EC%84%A4%EB%AA%85)\r\n\r\n입출력 예 #1\r\n\r\n![rect_5.png](6.png)\r\n\r\n캐릭터 위치는 (1, 3)이며, 아이템 위치는 (7, 8)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n입출력 예 #2\r\n\r\n![rect_7.png](7.png)\r\n\r\n캐릭터 위치는 (9, 7)이며, 아이템 위치는 (6, 1)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n입출력 예 #3\r\n\r\n[![rect_8.png](8.png)]\r\n\r\n캐릭터 위치는 (1, 1)이며, 아이템 위치는 (4, 7)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n## 구현 방법\r\n### 전체 직사각형들을 합친 것의 바깥쪽 테두리만 돌게하려면 어떻게 하는가?\r\n- rectangle을 순회하면서 범위 중 직사각형 내부는 0으로 테두리는 1로 board값을 갱신한다.\r\n- 이 때, 만일 `다른 직사각형의 내부안에 현재 직사각형의 테두리가 있지 않을 경우`에만 board를 1로 갱신한다. \r\n### 예외 사항\r\n![exception.png](exception.png)\r\n좌표가 인접한 경우 위와 같이 의도하지 않은 경우를 초래할 수도 있다.\r\n\r\n따라서 이를 해결하기 위해 모든 좌표의 값을 **2배**씩 해주어야 한다.\r\n\r\n이를 모두 반영하여 board 테이블을 만들면,\r\n\r\n```python\r\nboard = [[-1 for _ in range(102)] for _ in range(102)];\r\nfor r in rectangle:\r\n\t# map 객체를 통해 모든 좌표값에 2배\r\n\tx1,y1,x2,y2 = map(lambda x: x*2,r);\r\n for i in range(x1,x2+1):\r\n for j in range(y1,y2+1):\r\n\t # 직사각형 내부의 경우 0으로 개신\r\n if x1 {\r\n const [,triggerRender] = useState()\r\n let state = 'hello'\r\n\r\n function handleButtonClick() {\r\n state = 'hi'\r\n triggerRender()\r\n }\r\n return (\r\n <>\r\n

{state}

\r\n \r\n \r\n )\r\n}\r\n```\r\nuseState 반환값의 두 번째 원소를 실행해 리액트에 렌더링이 일어나게끔 변경했다. \r\n그럼에도 여전히 버튼 클릭시 state의 변경된 값이 렌더링되고 있지 않다. \r\n\r\n그 이유는 리액트의 렌더링은 **함수 컴포넌트에서 반환한 결과물인 return의 값을 비교해 실행되기 때문이다.** \r\n\r\n즉, 매번 렌더링이 발생할 때마다 함수는 새롭게 실행이 되고, 실행한 함수에서 state는 매번 hello로 초기화 되므로 아무리 state를 변경해도 hello로 초기화 되는 것이다. \r\n\r\n근데 렌더링이 될 때마다 초기화되는 변수(값)와는 달리, useState의 결과값은 어떻게 그 값을 유지할까?\r\n\r\n\r\n\r\n그럼 우리가 알고있는 useState는 대체 어떻게 구현이 되있는 것일지 한번 최대한 비슷하게 구현한 코드를 살펴보자.\r\n\r\n**먼저 useState의 결과 값이 유지되도록, state를 함수로 하여 state 값을 호출할 때마다 현재 state를 반환하게 해보자.**\r\n```js\r\nfunction useState(initialValue) {\r\n\tlet initialState = initialValue;\r\n\t\r\n\tfunction state() {\r\n\t\treturn initialState\r\n\t}\r\n\tfunction setState(newValue) {\r\n\t\tinitialState = newValue\r\n\t}\r\n\treturn [state, setState];\r\n}\r\nconst [value, setState] = useState(0);\r\nsetValue(1);\r\nconsole.log(value()); // 1\r\n```\r\n위의 코드도 나쁘진 않지만, 우리에게 익숙한 useState훅은 state를 함수가 아닌 상수처럼 사용하고 있다.\r\n어떻게 그게 가능한 걸까?\r\n\r\n## 클로저를 이용해 상태를 관리하는 useState\r\n이를 위해서 리액트는 **클로저**를 이용한 것이다. \r\nuseState는 클로저를 통해 useState 내부의 선언된 함수(setState)가 함수의 실행이 종료된 이후(useState가 호출된 이후)에도 지역변수인 state를 계속 참조할 수 있다.\r\n\r\nuseState 작동 방식을 대략적으로 흉내 낸 코드는 다음과 같다.\r\n\r\n```js\r\nconst MyReact = (function() {\r\n\tconst global = {}\r\n let index = 0\r\n \r\n function useState(initialState){\r\n \tif(!global.states) {\r\n \t// 애플리케이션 전체의 states 배열 초기화, 최초 접근이면 빈 배열로\r\n \tglobal.states = []\r\n }\r\n // states 정보를 조회해서, 현재 상태값이 있는지 확인\r\n // 없다면 초깃값으로 설정\r\n const currentState = global.states[index] || initialState\r\n // 위에서 조회한 값으로 states의 값 업데이트\r\n global.states[index] = currentState\r\n \r\n // 즉시실행함수로 setter 만듬\r\n const setState = (function() {\r\n \t// 클로저로 index를 가둬두어서 동일한 index에 접근이 가능\r\n \tlet currentIndex = index\r\n return function(value){\r\n \tglobal.states[currentIndex] = value\r\n //컴포넌트 렌더링이 들어가는 부분이다.(실제 코드는 생략)\r\n }\r\n })()\r\n // useState를 쓸 때마다 index를 하나씩 추가하는데, 이는 하나의 state마다\r\n // index가 할당되어있어, 그 index가 배열의 값(global.states)를 가리키고,\r\n // 필요할 때마다 그 값을 가져오게 하는 것이다.\r\n index = index + 1\r\n \r\n return [currentState,setState]\r\n}\r\n\r\nfunction Component() {\r\n\tconst [value, setValue] = useState(0);\r\n}\r\n})();\r\n```\r\n\r\n실제 리액트 코드에서는 useReducer를 이용해 구현되어 있어 약간의 차이가 있다.\r\n\r\n아무튼 여기서 함수의 실행이 끝났음에도 함수가 선언된 환경을 기억할 수 있는 방법이 바로 클로저인 것이다. 만약 클로저가 없다면, `setState`는 항상 `index`의 현재 값에 의존하게 된다. 즉, 컴포넌트가 여러 상태를 갖고 있을 때 마지막 `index`만 참조하므로, `setState`가 올바른 위치를 참조하지 않게 되는 것이다.\r\n\r\n매번 실행되는 함수 컴포넌트 환경에서 state의 값을 유지하고 사용하기 위해 리액트는 클로저를 활용하고 있다.\r\n\r\n`\r\n훅에 대한 구현체를 github에서 타고 올라가다보면 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 라는 문구를 만나게된다(무섭다 ㅋㅋ)\r\n위의 코드는 Preact의 구현을 기준으로 하고 있다. Preact는 react의 경량화 버전으로,\r\n대부분의 리액트 API를 지원하고 있다. \r\n`\r\n## 결론\r\nReact의 `useState`는 클로저를 통해 상태값을 안정적으로 유지하며, 함수 컴포넌트가 여러 번 호출되더라도 **각 상태값이 고유한 위치에 저장**될 수 있게 한다. `useState`가 반환하는 `setState` 함수는 생성 당시의 상태 위치(`index`)를 클로저로 캡처하여, 해당 상태값만 정확히 업데이트하도록 구현되어 있다.\r\n\r\n정리하자면, 클로저는 `setState`가 함수가 선언된 당시의 환경을 유지하게 해주기 때문에 **컴포넌트가 매번 재실행될 때마다 상태가 초기화되는 것을 방지**하고, 상태가 올바르게 유지될 수 있게 해준다.\r\n\r\n**참고**\r\n\r\n[서적] 모던 리액트 Deep Dive\r\n"},{"slug":"posts/Javascript/클로저에 대하여","title":"클로저에 대하여","date":"2024-10-25T20:00:24.000Z","image":"index.png","summary":"자바스크립트의 어렵지만 중요한 클로저의 개념과 활용 이해해보자","tags":["JavaScript","deepdive","클로저","생명주기"],"content":"# 클로저에 대하여\r\n\r\n면접 질문 중 단골이고, 자바스크립트에 관심이 있다면 한번쯤 들어봤을 개념인 **클로저**.\r\n\r\n사실 많이 난해한 개념이기도 하고, 필자 역시 전에 한번 공부를 해보았지만 아직 확실히 와닿지는 않는 개념이다. \r\n\r\n그래서 이번 기회에 제대로 정리하고 넘어가고자 한다. 함께 이 개념이 대체 뭔지 살펴보자.\r\n\r\n## 클로저의 정의\r\n> 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.\r\n\r\n'이게 뭔소리지?' 싶은가? 나도 그러하다. 위의 정의에서 이해하여야 할 핵심 키워드는 **함수가 선언된 렉시컬 환경**이다.\r\n\r\n ```js\r\n const x = 1;\r\n function outerFunc( ) {\r\n\t const x = 10;\r\n\t function innerFunc( ) {\r\n\t\t console.log(x); // 10\r\n\t}\r\n\tinnerFunc( );\r\n}\r\nouterFunc( );\r\n```\r\n`outerFunc` 내부에서 중첩 함수 `innerFunc`가 정의되고 호출되었다. \r\n\r\n따라서 중첩 함수 `innerFunc` 내부에서 자신을 포함하고 있는 `outerFunc`의 x 변수에 접근할 수 있다. **만약 innerFunc 함수가 outerFunc의 내부에서 정의되지 않았다면, 즉 외부에서 별도로 정의 되었다면** innerFunc를 outerFunc 내부에서 호출해도 outerFunc 함수의 변수에 접근이 불가능하다.\r\n\r\n ```js\r\n const x = 1;\r\n function outerFunc( ) {\r\n\t const x = 10;\r\n\t// 안에서 호출되었지만 접근 불가능\r\n\t innerFunc( );\r\n}\r\nfunction innerFunc( ) {\r\n\t// 상위 스코프인 전역에서 선언된 1\r\n\tconsole.log(x); // 1\r\n}\r\nouterFunc( );\r\n```\r\n\r\n위와 같은 현상은 자바스크립트가 **렉시컬 스코프**를 따르기에 발생한다.\r\n\r\n## 렉시컬 스코프\r\n자바스크립트 엔진은 함수를 **어디서 호출했느냐**가 아니라 함수를 **어디서 정의했는지**에 따라 **상위 스코프를 결정** 한다. \r\n\r\n이를 **렉시컬 스코프**라고 한다.\r\n\r\n위의 예제코드를 다시한번 본다면, outerFunc와 innerFunc는 모두 전역에서 정의 되었고, 함수의 상위 스코프는 함수를 어디서 정의했는지에 따라 결정되므로 **두 함수의 상위 스코프는 모두 전역이다.**\r\n\r\n함수의 상위 스코프는 결국, **함수의 정의된 위치에 따라 정적으로 결정**되고, 함수의 호출된 위치는 어떠한 영향도 주지 못한다.\r\n\r\n>렉시컬환경: **변수를 저장하고 외부 스코프와 연결을 유지하는 객체**라고 생각하면된다. 자바스크립트에서는 함수가 생성될 때마다 렉시컬 환경이 만들어지며, 함수 내부의 변수뿐 아니라 함수가 선언된 위치에 있는 외부 변수도 기억하게된다.\r\n>\r\n> 코드가 위치한 곳에 따라 **변수와 함수를 어디서 찾아볼지 알려주는 일종의 \"지도\"**라고 생각할 수도 있다\r\n\r\n\r\n렉시컬 환경은 자신의 **외부 렉시컬 환경에 대한 참조**를 통해 상위 렉시컬 환경과 연결이된다.\r\n\r\n따라서 함수의 상위 스코프를 결정한다는 것은, 현재 함수의 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값을 결정한다는 것을 의미한다.\r\n\r\n렉시컬 스코프를 다시한번 정의해보자면, 렉시컬 환경의 \"외부 렉시컬 환경에 대한 참조\"에 저장한 참조값, 즉 상위 스코프에 대한 참조는 **함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)**에 의해 결정되는 것이라고 할 수 있다. \r\n\r\n\r\n\r\n**함수는 자신의 내부슬롯 [[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.** \r\n이곳을 참조해서 자신이 호출되었을 때 생성될 함수 렉시컬환경의 \"외부 렉시컬 환경에 대한 참조\"에 저장될 참조값을 보고, 자신이 존재하는 한, 이 [[Environment]] 슬롯에 저장한 렉시컬 환경의 참조, 즉 **상위 스코프**를 기억한다.\r\n\r\n## 클로저와 렉시컬환경\r\n그렇다면 다음의 코드를 살펴보자\r\n```js\r\nconst x = 1 ;\r\nfunction outer() {\r\n\tconst x = 10;\r\n\tconst inner = function ( ) {console.log(x)}\r\n\t//inner함수 반환\r\n\treturn inner;\r\n}\r\n// outer함수를 호출하면 중첩 함수 inner를 반환한다.\r\n// 그리고 outer 함수의 실행 컨텍스트는 제거된다.\r\nconst innerFunc = outer( );\r\ninnerFunc( )// 10\r\n```\r\nouter함수를 호출하면 outer함수는 중첩함수 inner를 반환하고 생명주기를 마감한다. \r\n\r\n즉 outer함수의 실행이 종료되었으므로, 실행컨텍스트가 제거된다. (실행컨텍스트 스택에서 pop된다.)\r\n\r\n이때 outer 함수의 지역변수x 역시 생명주기를 마감했으므로, 실행 컨텍스트가 제거되어 유효하지 않아 보인다.\r\n\r\n그러나 위의 실행 결과는 outer 지역 변수x 의 값인 10을 반환한다. 이미 생명 주기가 종료되어 outer 함수의 지역변수 x가 실행 컨텍스트 스택에서 제거되었는데도 다시 부활이라도 한 것 마냥 말이다. \r\n\r\n이처럼 **외부 함수보다 중첩함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명주기가 종료한 외부함수의 변수를 참조**할 수 있다. \r\n\r\n이러한 **중첩 함수를 클로저**라고 부른다.\r\n\r\n다시 정의로 돌아가보자.\r\n> 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.\r\n\r\n위 정의를 예제에 대입해보자면 `함수`는 반환된 `중첩함수(inner)`를 의미하고 `그 함수가 선언될 때의 렉시컬 환경(Lexical environment)`란 그 `중첩 함수(inner)가 정의됐을 때의 스코프`를 의미하는 것이다.\r\n\r\n즉, **클로저는 반환된 중첩 함수가 자신이 선언됐을 때의 렉시컬 환경 즉, 스코프를 기억하여 자신이 선언됐을 때의 렉시컬 환경 밖에서 호출되어도 그 렉시컬 환경(스코프)에 접근할 수 있는 함수**를 말한다. \r\n\r\n조금 더 간단히 말하면 **클로저는 자신이 생성될 때의 상위 스코프(렉시컬 환경)을 기억하는 함수이다**\r\n\r\n\r\n\r\n\r\ninner 함수의 [[Environment]] 슬롯이 outer 함수의 렉시컬 환경을 참조하고, inner가 전역 변수 innerFunc에 저장되어 계속 사용되고 있으므로, 이와 연결된 모든 요소는 가비지 컬렉터에 의해 제거되지 않습니다.\r\n\r\n\r\n> **가비지 컬렉터(Garbage Collector)는 프로그램이 더 이상 사용하지 않는 메모리를 자동으로 해제해 주는 자바스크립트 엔진의 기능**이다.\r\n> \r\n> 가비지 컬렉터는 특정 메모리 공간이 더 이상 참조되지 않을 때 그 공간을 \"가비지\"로 판단하여 메모리를 해제한다. 반대로, **누군가가 참조하고 있는 메모리 공간은 함부로 해제하지 않는다**.\r\n\r\n클로저는 자바스크립트의 강력한 기능으로, 필요하다면 적극 활용해야 한다. 클로저가 유용하게 사용되는 상황을 살펴보자.\r\n\r\n## 클로저의 활용\r\n\r\n### 상태를 안전하게 변경하고 유지할 때\r\n\r\n클로저는 **상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고, 특정 함수에게만 상태 변경을 허용하여, 상태를 안전하게 변경하고 유지할 수 있도록 사용한다.**\r\n```js\r\nconst counter = (function ( ) {\r\n// 은닉된 상태 (외부에서 접근 불가)\r\nlet num = 0;\r\n// 클로저인 메서드를 갖는 객체를 반환한다.\r\n// 객체 리터럴은 스코프를 만들지 않는다.\r\n// 따라서 아래 메서드들의 상위 스코프는 즉시 실행 함수의 렉시컬 환경이다.\r\nreturn {\r\n\tincrease() {\r\n\t\treturn ++num;\r\n\t}\r\n\tdecrease() {\r\n\t\treturn num>0? --num: 0;\r\n\t}\r\n}());\r\n\r\nconsole.log(counter.increase()); // 1\r\nconsole.log(counter.decrease()); // 2\r\n\r\nconsole.log(counter.decrease()); // 1\r\nconsole.log(counter.decrease()); // 0\r\n```\r\n즉시 실행 함수는 호출된 이후 소멸되지만, 즉시 실행 함수가 반환한 클로저(increase, decrease)는 **자신의 상위 스코프인 즉시 실행함수의 렉시컬 환경을 기억하고 있다**. \r\n\r\n이 코드에서 \r\n- 카운터 상태(num 변수의 값)은 increase, decrease 함수가 호출되기 전까지 변경되지않고 유지되며 **외부에서 접근할 수 없다**.\r\n- 카운터 상태는 오직 **increase, decrease로 정의된 함수로만 변경이 가능**하다.\r\n\r\n\r\n\r\n이처럼 클로저는 **상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고 특정함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용**된다.\r\n\r\n### 전역변수의 사용을 억제할 때\r\n\r\n클로저는 전역 변수 사용을 억제하고 대신 **함수 내부의 변수로 상태를 관리**할 수 있도록 도와준다. 이렇게 하면 전역 변수를 사용하지 않고도 데이터가 안전하게 유지되며, 다른 코드와 충돌하지 않는 이점을 얻을 수 있다.\r\n\r\n```js\r\nlet counter = 0; // 전역 변수\r\n\r\nfunction incrementCounter() {\r\n counter += 1;\r\n return counter;\r\n}\r\n\r\nconsole.log(incrementCounter()); // 1\r\nconsole.log(incrementCounter()); // 2\r\nconsole.log(counter); // 전역 변수에 접근 가능 (위험)\r\n```\r\n위 코드에서는 `counter`가 전역에 선언되어 있기 때문에 어디서든 접근 가능하여, 이러면 다른 코드에서 `counter`를 실수로 변경할 위험이 있다.\r\n```js\r\nfunction createCounter() {\r\n let counter = 0; // 함수 내부 변수로 관리\r\n\r\n return function() {\r\n counter += 1;\r\n return counter;\r\n };\r\n}\r\n\r\nconst incrementCounter = createCounter();\r\n\r\nconsole.log(incrementCounter()); // 1\r\nconsole.log(incrementCounter()); // 2\r\nconsole.log(typeof counter); // 'undefined' - 전역에서 접근 불가\r\n```\r\n\r\n이 코드에서는 `counter` 변수가 **`createCounter` 함수 내부에만 존재**하므로 외부에서 직접 접근하거나 수정할 수 없다. \r\n\r\n대신, `incrementCounter` 함수는 **클로저를 통해 `counter`를 기억**하고 있으므로 호출할 때마다 `counter`를 안전하게 증가시킬 수 있다.\r\n\r\n이처럼, 클로저를 사용하면 함수 내 지역 변수를 통해 상태를 관리하게 되어 **전역 변수를 사용하지 않아도 안전하게 데이터 상태를 유지**할 수 있다.\r\n\r\n### React의 useState\r\n\r\n이런 이점을 활용하여 react의 useState에도 클로저가 활용된다.\r\n\r\n`useState`를 사용하면 컴포넌트 내에서 **상태를 관리**하게 되는데, 이 상태가 **컴포넌트가 렌더링될 때마다 유지**되도록, 또한 `setState` 함수로만 상태가 변경되도록 하는 데에 클로저가 활용되는 것이다.\r\n\r\n이와 관련하여서는 다음 포스팅에서 자세하게 다룰 예정이다.\r\n\r\n\r\n## 결론\r\n\r\n지금까지 자바스크립트의 어렵지만 주요한 개념인 클로저에 대해 살펴보았다.\r\n\r\n**클로저**는 **함수와 그 함수가 선언된 렉시컬 환경의 조합으로, 자바스크립트에서 함수는 자신이 정의된 위치에 따라 상위 스코프를 결정하는 렉시컬 스코프**를 따르므로, \r\n중첩 함수가 외부 함수의 스코프를 참조하여 외부함수가 생명주기가 끝났음에도 그 함수의 변수를 참조할 수 있는 함수를 의미한다."},{"slug":"posts/React/[React DeepDive] React의 가상 DOM","title":"[React DeepDive] React의 가상 DOM","date":"2024-09-25T12:00:24.000Z","image":"React.svg","summary":"리액트 딥다이브 책 내용 중 '가상 DOM과 파이버'의 정리 내용입니다.","tags":["react","deepdive","Virtual DOM"],"content":"# [React DeepDive] React의 가상 DOM\r\n\r\n\r\n리액트의 특징 중 하나는 실제 DOM이 아닌 가상 DOM을 운영한다는 것이다. 이번 글에서는 React DeepDive에서 다루는 **가상 DOM이 무엇인지**, **그리고 실제 DOM에 대해 어떤 이점이 있는지 살펴보고**, 가상 DOM을 다룰 때 **주의할 점**에 대해서도 다루려고 한다.\r\n\r\n## DOM과 브라우저 렌더링 과정\r\n> **DOM**: 웹페이지에 대한 인터페이스로 브라우저가 웹페이지의 콘텐츠와 구조를 어떻게 보여줄지에 대한 정보를 담고 있다.\r\n\r\n브라우저가 웹사이트 접근 요청을 받고 화면을 그리는 과정은 다음과 같다.\r\n\r\n\"트리를\r\n\r\n\r\n1. 브라우저가 사용자 요청한 주소에서 HTML파일을 다운로드한다.\r\n\r\n2. 브라우저의 렌더링 엔진이 HTML을 파싱해 DOM노드로 구성된 트리 즉, `DOM`을 만든다.\r\n3. 2번 과정에서 CSS파일을 만나면 해당 CSS 파일도 다운로드한다.\r\n4. 브라우저의 렌더링 엔진이 이 CSS역시 파싱해 CSS 노드로 구성된 트리 즉, `CSSOM`을 만든다\r\n5. 브라우저는 DOM 노드를 순회하는데 모든 노드가 아닌, **사용자 눈에 보이는 노드만**(display:none과 같은 노드는 방문 x) 방문한다.\r\n6. 눈에 보이는 노드를 대상으로 해당 노드에 대한 CSSOM 정보를 찾고 여기서 발견한 CSS 스타일 정보를 이 노드에 적용한다. \r\n>\t- `레이아웃`: 각 노드가 브라우저 화면의 어느 좌표에 정확히 나타나야 하는지 계산하는 과정이며, 이 과정을 거치면 **페인팅 과정도 거치게 된다.**\r\n>- `페인팅`: 레이아웃 단계를 거친 노드의 색과 같은 **실제 유효한 모습을 그리는 과정**\r\n\r\n## 가상 DOM의 탄생 배경\r\n\r\n### 웹페이지를 추가로 렌더링 하는데 드는 비용\r\n앞에서 살펴본 브라우저가 웹페이지를 렌더링하는 과정은 매우 복잡하고 많은 비용이 든다. \r\n\r\n또한 정보를 보여주는데 그치지 않고 사용자의 인터렉션을 통해 다양한 정보를 노출해야 하기에, 렌더링이 완료된 이후에도 **사용자의 인터렉션으로 웹페이지가 변경되는 상황** 또한 고려해야 한다.\r\n\r\n이 과정에서 예를 들어,\r\n- 특정 요소의 색상이 변경되는 경우: 페인팅만이 일어나서 빠른처리가 가능하다.\r\n\r\n- **특정 요소의 노출 여부나 사이즈가 변경되는 경우**: 레이아웃이 일어나고, 레이아웃은 **필연적으로 리페인팅을 발생**하기 때문에 더 많은 비용이 든다.\r\n\r\n- DOM 변경이 일어나는 요소가 **자식 요소를 많이 가지고 있는 경우**: 하위 자식 요소 역시 변경되야 해서 더 많은 비용 지불\r\n\r\n이러한 추가 렌더링 작업은 하나의 페이지에서 모든 작업이 일어나는 `싱글 페이지 애플리케이션(SPA)`에서 더 많아진다. \r\n\r\n페이지가 변경될 때 처음부터 HTML을 새로 받아서 다시금 렌더링 과정을 시작하는 일반적인 웹페이지와는 다르게, 하나의 페이지에서 계속해서 요소의 위치를 재계산하게 된다.\r\n\r\n그러므로 라우팅이 변경되는 경우 고정된 헤더와 같은 요소들을 제외하고 대부분의 요소를 삭제, 삽입 및 요소의 위치를 다시 계산해야 하므로, DOM을 관리하는 과정에서 부담하는 비용이 커진다.\r\n\r\n### 가상 DOM의 탄생\r\n사용자의 인터렉션에 따라 DOM의 모든 변경 사항을 추적하는 것은 개발자에게는 너무나 수고스러운 일이다. 그렇기에 모든 DOM의 변경보다 **결과적으로 만들어지는 DOM 결과물 하나만 아는 것**이 개발자의 입장에서 더 유용할 것이다.\r\n\r\n이것을 해결하기 위해 탄생한 것이 바로 `가상 DOM`이다. \r\n가상 DOM은 실제 브라우저가 아닌 리액트가 관리하는 가상의 DOM을 의미한다.\r\n\r\n가상 DOM은 웹페이지가 표시해야 할 DOM을 일단 **메모리에 저장**하고, 리액트가 실제 변경에 대한 준비가 완료되었을 때, 실제 브라우저의 DOM에 반영한다.\r\n\r\n이렇게 DOM 계산을 메모리에서 계산하는 과정을 한 번 거치게 된다면 실제로는 여러 번 발생했을 렌더링 과정을 최소화하여 부담을 덜 수 있다.\r\n\r\n> 가상 DOM은 **일반적인 브라우저보다 무조건 항상 빠르지는 않다**. \r\n>\r\n> 무조건 빠른 것이 아닌, 대부분의 상황에서 웬만한 애플리케이션을 만들 정도로 충분히 빠르다고 보는 것이 옳다.\r\n\r\n## 가상 DOM을 위한 아키텍처, 리액트 파이버\r\n가상 DOM과 렌더링 과정 최적화를 가능하게 해주는 것이 `리액트 파이버`이다.\r\n\r\n`리액트 파이버`는 리액트에서 관리하는 평범한 자바스크립트 객체이다. \r\n`파이버`는 `파이버 재조정자(fiber reconciler)`가 관리하는데, 가상 DOM과 실제 DOM을 비교하여 변경에 관련된 정보를 가지고 있는 파이버를 기준으로 화면에 렌더링을 요청하는 역할을 한다.\r\n\r\n> 재조정(reconcilation): 리액트에서 어떤 부분을 새로 렌더링 해야하는지 가상 DOM과 실제 DOM을 비교하는 과정 \r\n\r\n파이버는 애니메이션,레이아웃,사용자 인터랙션에 올바른 결과물을 만드는 반응성 문제를 해결하며, 다음과 같은 일을 할 수 있다.\r\n- 작업을 작은 단위로 쪼개고 **우선순위를 매긴다.**\r\n\r\n- 이 작업들은 일시 정지 및 다시 시작이 가능하다.\r\n\r\n- 이전 작업을 재사용 하거나 필요 없는 경우에는 폐기할 수 있다.\r\n\r\n이러한 모든 과정은 **비동기로 일어난다**. 과거에는 이러한 조정 알고리즘이 동기적인 스택 알고리즘으로 이뤄져 있었고, 동기적으로 작업이 이뤄졌기에 자바스크립트의 싱글 스레드의 특징 상 수행 중인 작업은 중단될 수 없었다. \r\n이러한 문제 때문에 리액트 팀은 스택 조정자 대신 파이버라는 개념을 탄생시킨다.\r\n\r\n### 파이버는 어떻게 구현되어 있을까?\r\n`파이버`는 일단 하나의 작업 단위로 구성되어 있다. 리액트는 작업 단위를 하나씩 처리하고, **finishWork( )** 라는 작업으로 마무리한다. 그리고 이 작업을 커밋해 브라우저 DOM에 가시적인 변경 사항을 만들어 낸다. \r\n이러한 단계는 두 단계로 나뉘는데,\r\n- `렌더 단계`: 사용자에게 노출되지 않는 모든 **비동기** 작업을 수행하고, **우선 순위를 지정하거나 중지시키거나 버리는 등의 작업이 일어난다.**\r\n- `커밋 단계`: DOM에 실제 변경 사항을 반영하기 위한 작업, commitWork( )가 실행되는데, **동기식**으로 일어나고 중단될 수 없다.\r\n\r\n파이버는 실제 리액트 코드에서 이렇게 구현되어 있다.\r\n```js\r\nfunction FiberNode(tag, pendingProps, key, mode) {\r\n // Instance\r\n this.tag = tag;\r\n this.key = key;\r\n this.elementType = null;\r\n this.type = null;\r\n this.stateNode = null;\r\n\r\n // Fiber\r\n this.return = null;\r\n this.child = null;\r\n this.sibling = null;\r\n this.index = 0;\r\n\r\n this.ref = null;\r\n\r\n this.pendingProps = pendingProps;\r\n this.memoizedProps = null;\r\n this.updateQueue = null;\r\n this.memoizedState = null;\r\n this.dependencies = null;\r\n\r\n this.mode = mode;\r\n\r\n // Effects\r\n this.effectTag = NoEffect;\r\n this.nextEffect = null;\r\n\r\n this.firstEffect = null;\r\n this.lastEffect = null;\r\n\r\n this.expirationTime = NoWork;\r\n this.childExpirationTime = NoWork;\r\n\r\n this.alternate = null;\r\n}\r\n```\r\n위와 같이 파이버는 단순한 자바스크립트 객체로 구성되어 있다. \r\n파이버와 리액트 요소의 한가지 중요한 차이점은 `리액트 요소`는 렌더링이 발생할 때 마다 새롭게 생성되지만, `파이버`는 컴포넌트가 최초로 마운트 되는 시점에 생성되어 **가급적이면 재사용된다**는 것이다.\r\n\r\n```js\r\nfunction createFiber(tag, pendingProps, key, mode) {\r\n return new FiberNode(tag, pendingProps, key, mode);\r\n}\r\n```\r\n```js\r\nfunction createFiberFromElement(element, mode, expirationTime) {\r\n let owner = null;\r\n const type = element.type;\r\n const key = element.key;\r\n const pendingProps = element.props;\r\n const fiber = createFiberFromTypeAndProps(\r\n type,\r\n key,\r\n pendingProps,\r\n owner,\r\n mode,\r\n expirationTime,\r\n );\r\n\r\n return fiber;\r\n}\r\n```\r\n이제 여기서 선언된 주요 속성을 살펴보면서 어떤 내용을 담고 있는지 살펴보자.\r\n\r\n- `tag` : 파이버는 하나의 element에 하나의 파이버가 생성되어 1:1 관계를 가진다. 여기서 **1:1로 매칭된 정보를 가지고 있는 것**이 tag필드이다. 연결되는 것은 컴포넌트, DOM노드 등이 될 수 있는데, 될 수 있는 것들은 다음과 같다.\r\n- `stateNode`: 이 속성은 파이버 자체에 대한 참조 정보를 가지고 있다.\r\n- `child`, `sibling`, `return` : 파이버 간의 관계 개념을 나타내는 속성이다. 파이버는 트리 형식을 구성하는데 이 트리 형식을 구성하는데 필요한 정보가 이 속성 내부에 적용된다. 한 가지 리액트 컴포넌트 트리와 다른 점은 children이 없다는 것 즉 **단 하나**의 `child`만이 존재한다는 것이다.\r\n\r\n\t여러 개의 자식이 존재할 경우 항상 **첫 번째 자식의 참조**로 구성되며, 나머지는 자식들은 동등한 `sibling`으로 구성, `return`은 부모 파이버를 의미한다.\r\n\r\n- `index` : 여러 형제들 사이에서 자신의 위치를 나타낸다.\r\n- `pedingProps` : 아직 처리하지 못한 props\r\n- `memoizedProps` : pendingProps를 기준으로 렌더링이 완료된 이후에 pendingProps를 memoizedProps에 저장하여 관리한다.\r\n- `updateQueue` : 상태 업데이트, 콜백 함수, DOM 업데이트 등 작업을 담아두는 Queue.\r\n- `memoizedState` : 함수 컴포넌트의 훅 목록이 저장된다.\r\n- `alternate`: 뒤이어 설명할 리액트 파이버 트리와 이어질 개념. 리액트의 트리는 두 개인데, alternate는 반대 트리의 파이버를 가리킨다.\r\n\r\n이렇게 생성된 파이버는 state가 변경되거나 생명주기 메서드가 실행되거나 DOM의 변경이 필요한 시점 등에 실행된다. \r\n이러한 작업들은 작은 단위로 나눠서 처리할 수도, 애니메이션과 같이 우선순위가 높은 작업은 빨리 처리하거나, 낮은 작업을 연기시키는 등 좀 더 유연하게 처리된다.\r\n\r\n\r\n리액트 개발 팀은 사실 리액트는 가상 DOM이 아닌 Value UI, 즉 값을 가지고 있는 UI를 관리하는 라이브러리라는 내용을 피력한 바가 있다. \r\n\r\n즉, 리액트의 핵심원칙은 **UI를 문자열, 숫자, 배열과 같은 값으로 관리한다는 것**이다. \r\n변수에 이러한 UI관련 값을 보관하고, 리액트의 자바스크립트 코드 흐름에 따라 이를 관리하고, 표현하는 것이 바로 리액트다.\r\n\r\n### 파이버 트리\r\n파이버 트리는 하나는 **현재 모습을 담은** 파이버 트리, 다른 하나는 작업 중인 상태를 나타내는 **workInProgress** 트리다. 작업이 끝나면, 리액트는 **단순히 포인터만 변경**해 workInProgress 트리를 **현재 트리**로 바꿔버린다. 이러한 기술을 **더블 버퍼링**이라고 한다.\r\n\"트리를\r\n\r\n리액트에서는 미처 다 그리지 못한 모습을 노출시키지 않기 위해 (불완전한 트리를 보여주지 않기 위해) 더블 버퍼링 기법을 쓰는데, 이러한 더블 버퍼링을 위해 트리가 두 개 존재하며, 이 더블 버퍼링은 커밋 단계에서 수행된다.\r\n\r\n- 먼저 현재 UI 렌더링을 위해 존재 하는 **current**를 기준으로 모든 작업이 시작된다.\r\n- 업데이트가 발생하면 파이버는 리액트에서 새로 받은 데이터로 새로운 **workInProgress** 트리를 빌드하기 시작한다.\r\n- 빌드하는 작업이 끝나면 다음 렌더링에 이 트리를 사용한다.\r\n- 빌드된 workInProgress 트리가 UI에 최종적으로 렌더링되어 반영이 완료되면 **current가 이 workProgress로 변경된다**.\r\n\r\n### 파이버의 작업 순서\r\n일반적인 파이버 노드의 생성흐름은 다음과 같다.\r\n1. 리액트는 `beginWork()` 함수를 실행해 파이버 작업을 수행하고, 더 이상 자식이 없는 파이버를 만날 때까지 트리 형식으로 시작된다.\r\n2. 1번의 작업이 끝나면 `completeWork( )` 함수를 실행해 파이버 작업을 완료한다.\r\n3. 형제가 있다면 형제로 넘어간다.\r\n4. 2,3번이 끝난다면 `return`으로 돌아가 자신의 작업이 완료되었음을 알린다.\r\n\r\n이러한 작업으로 트리가 생성이 되었는데, setState 등으로 업데이트가 발생하면 어떻게 될까? 이미 앞서 만든 current트리가 존재하고, setState로 업데이트 요청을 받아 workInProgress트리를 다시 빌드하기 시작한다. \r\n\r\n최초 렌더링 시에는 모든 파이버를 새로 만들어야 했지만 이제는 파이버가 이미 존재하므로 되도록 새로 생성하지 않고, **기존 파이버에서 업데이트 된 props를 받아 파이버 내부에서 처리한다**.\r\n\r\n이처럼, 재조정 작업 때마다 새롭게 파이버 자바스크립트 객체를 만드는 것 이 아닌, 기존의 객체를 재활용하기 위해 내부 속성값만 초기화하거나 바꾸는 형태로 트리를 업데이트한다.\r\n\r\n과거에는 이 작업을 동기식으로 처리했고, 트리 업데이트 과정 및 새로운 트리를 만드는 작업은 동기식이고 중단될 수 없다. 그러나 현재는 우선순위가 높은 다른 업데이트가 오면 현재 업데이트 작업을 일시 중단하거나 새로 만들거나 폐기할 수 있으며 작업 단위를 파이버 단위로 나누어 우선순위를 할당하는 것 역시 가능하다.\r\n\r\n### 파이버와 가상 DOM\r\n리액트 컴포넌트에 대한 정보를 **1:1로 가지고 있는 것**이 파이버이며, 이 파이버는 리액트 아키텍처 내부에서 **비동기**로 이뤄진다. 이와 달리 실제 브라우저 구조인 DOM에 반영하는 것은 동기적으로 이뤄져야 하기에, 메모리 상에서 이 작업을 먼저 수행해서 최종적인 결과물만 실제 브라우저 DOM에 적용하는 것이다.\r\n\r\n> 사실 가상 DOM은 오직 웹 어플리케이션에서만 통용되는 개념이고, 리액트 파이버는 리액트 네이티브와 같은 브라우저가 아닌 환경에서도 사용할 수 있기에 엄밀히 하면 파이버와 가상 DOM은 동일한 개념이 아니다. \r\n\r\n### 정리\r\n결국 가상 DOM과 리액트의 핵심은 **브라우저의 DOM을 더욱 빠르게 그리고 반영하는 것이 아니라 바로 값으로 UI를 표현하는 것**이다. \r\n\r\n화면에 표시되는 UI를 자바스크립트의 문자열, 배열 등과 마찬가지로 값으로 관리하고 이러한 흐름을 효율적으로 관리하기 위한 메커니즘이 바로 리액트의 핵심이다."}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/gold.json b/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/gold.json deleted file mode 100644 index 5c111e3..0000000 --- a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/gold.json +++ /dev/null @@ -1 +0,0 @@ -{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs"],"currentTag":"gold","posts":[{"slug":"posts/Algorithm/[백준 gold 5] 빌런 호석","title":"[백준 gold 5] 빌런 호석","date":"2024-08-09T17:05:24.000Z","image":"index.png","summary":"dfs을 활용한 프로그래머스 [level 3] 징검다리 건너기 문제 풀이 입니다.","tags":["Algorithm","백준","gold","dfs"],"content":"# [gold 3] 빌런 호석\r\n\r\n## 문제 설명\r\n\r\n치르보기 빌딩은 11층부터 NN층까지 이용이 가능한 엘리베이터가 있다. 엘리베이터의 층수를 보여주는 디스플레이에는 KK 자리의 수가 보인다. 수는 00으로 시작할 수도 있다. 00부터 99까지의 각 숫자가 디스플레이에 보이는 방식은 아래와 같다. 각 숫자는 7개의 표시등 중의 일부에 불이 들어오면서 표현된다.\r\n\r\n![](1.png)\r\n\r\n예를 들어 K=4K=4인 경우에 16801680층과 501501층은 아래와 같이 보인다.\r\n\r\n![](2.png)\r\n\r\n\r\n\r\n빌런 호석은 치르보기 빌딩의 엘리베이터 디스플레이의 LED 중에서 최소 11개, 최대 PP개를 반전시킬 계획을 세우고 있다. 반전이란 켜진 부분은 끄고, 꺼진 부분은 켜는 것을 의미한다. 예를 들어 숫자 11을 22로 바꾸려면 총 5개의 LED를 반전시켜야 한다. 또한 반전 이후에 디스플레이에 올바른 수가 보여지면서 11 이상 NN 이하가 되도록 바꿔서 사람들을 헷갈리게 할 예정이다. 치르보기를 사랑하는 모임의 회원인 당신은 호석 빌런의 행동을 미리 파악해서 혼쭐을 내주고자 한다. 현재 엘리베이터가 실제로는 XX층에 멈춰있을 때, 호석이가 반전시킬 LED를 고를 수 있는 경우의 수를 계산해보자.\r\n\r\n### 입력\r\n\r\n N,K,P,XN,K,P,X 가 공백으로 구분되어 첫째 줄에 주어진다.\r\n\r\n### 출력\r\n\r\n호석 빌런이 엘리베이터 LED를 올바르게 반전시킬 수 있는 경우의 수를 계산해보자.\r\n\r\n## 접근 방법\r\n\r\n숫자에 따른 LED를 먼저 구현하기 위해, \r\n\r\n그림과 같이, 각 LED의 위치에 번호를 매겨, 배열안에 그 번호가 있으면 불이 켜져있는 것으로, 없다면 불이 꺼져 있는 것으로 표현하였다.\r\n\"이미지\r\n\r\n\r\n그림처럼 0을 표현하면 3번자리를 제외한 모든 번호가 켜져있으므로, `[0,1,2,4,5,6]`으로 표현 할 수 있다.\r\n\r\n각 번호를 이와 같이 바꾸어 numbers 배열에 저장하면, 이와 같다.\r\n```python\r\nnumbers = [[0,1,2,4,5,6],[2,5],[0,2,3,4,6],[0,2,3,5,6],[1,2,3,5],[0,1,3,5,6],[0,1,3,4,5,6],[0,2,5],[0,1,2,3,4,5,6],[0,1,2,3,5,6]];\r\n```\r\n이후 만일 현재 번호와 비교할 번호의 LED를 비교하기 위해 서로의 차집합을 더해서 두 번호가 서로를 비교했을 때, 가지고 있는 혹은 없는 번호의 개수를 구한다.\r\n```python\r\ncur_count = len(list(cur.difference(compare))) + len(list(compare.difference(cur)));\r\n```\r\n이와 같은 방식으로 반전시킬 LED의 개수를 구하고, dfs를 활용하여 x의 모든 자릿수를 순회하여 p보다 작거나 같게 LED를 반전하면서, n보다 번호가 작거나 같다면 result를 1 증가 시킨다.\r\n\r\n전체코드는 다음과 같다.\r\n```python\r\nimport sys;\r\ninput = sys.stdin.readline;\r\n\r\nn,k,p,x = map(int, input().split());\r\nresult = 0;\r\nnumbers = [[0,1,2,4,5,6],[2,5],[0,2,3,4,6],[0,2,3,5,6],[1,2,3,5],[0,1,3,5,6],[0,1,3,4,5,6],[0,2,5],[0,1,2,3,4,5,6],[0,1,2,3,5,6]];\r\n\r\nx = list(str(x));\r\nif len(x) != k:\r\n\t# k 보다 자릿수 x가 자릿수가 작다면 작은 만큼 앞에 0을 채워주어야 한다.\r\n for _ in range(k-len(x)):\r\n x.insert(0,'0');\r\n \r\ndef dfs(index,count,st):\r\n global result;\r\n if count>p:\r\n return;\r\n if index == k:\r\n if 0 0:\r\n time += w_count*(times[i-1] + times[i]) + times[i];\r\n else:\r\n time += times[i];\r\n if time>limit:\r\n l = level+1;\r\n else:\r\n r = level;\r\n answer = level;\r\n return answer;\r\n ```\r\n\r\n## 느낀 점\r\n한 동안 알고리즘 문제를 안풀었더니 뇌가 굳은게 아닌가 싶다. 그리고 문제 설명 자체가 길고 복잡하면 살짝 길을 잃는 습관이 있는데, 이번 문제에서도 이해해보면 그렇게 어렵지 않은 것 같음에도 불구하고 그런 습관이 나타난 것 같다. \r\n\r\n전에 백준에서 풀었던 이분탐색문제랑 거의 흡사했음에도 이분탐색을 빨리 떠올리지 못한게 아쉽다."}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/useState.json b/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/useState.json deleted file mode 100644 index a6cc504..0000000 --- a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/useState.json +++ /dev/null @@ -1 +0,0 @@ -{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs","all"],"currentTag":"useState","posts":[{"slug":"posts/React/React useState 훅과 클로저","title":"React useState 훅과 클로저","date":"2024-10-26T18:00:24.000Z","image":"index.png","summary":"클로저를 이용한 React useState 훅의 작동 원리를 이해해보자","tags":["react","JavaScript","deepdive","useState","hook","클로저"],"content":"# React useState 훅과 클로저\r\n\r\n클로저에 대해 개념은 알고 있었지만, 사실 면접 질문때나 쓰이는 것이고, 실제로 어떻게 쓰이는지에 대해서는 와닿지 못한 부분도 있었다. 그러던 도중 react deep dive를 공부하며, react 개발자가 아마도 가장 많이 사용하는 **useState**에 클로저가 쓰인다는 사실을 알았다! \r\n어떻게 쓰이는 지 함께 살펴보자.\r\n\r\n\r\n\r\n## 상태값을 어떻게 관리할까\r\n보통 useState의 기본 사용법은 다음과 같을 것이다\r\n```js\r\nimport { useState } from 'react'\r\n\r\nconst [state, setState] = useState\r\n```\r\n인수로 state의 초깃값을 넘겨주고, 만일 아무것도 안넘겨주면 초깃값은 undefined일 것이다.\r\n훅의 반환 값은 배열이고, 배열의 첫 번째 원소는 state 값 자체이며, 두 번째 원소는 setState 함수를 통해 해당 state 값을 변경할 수 있다.\r\n\r\n\r\n**리액트에서 렌더링은 함수 컴포넌트의 return을 실행한 다음, 실행 결과를 이전의 리액트 트리와 비교해 리렌더링이 필요한 부분만 업데이트해 이뤄진다.**\r\n그렇기 때문에 렌더링 방식이랑 메커니즘이 다른 **변수**를 통해서 상태값을 관리하는 것은 적절하지 못하다. (이전 글 react virtualDOM 참고)\r\n\r\n그렇다면 다음 코드를 살펴보자.\r\n\r\n```js\r\nimport React from 'react'\r\n\r\nconst Component = () => {\r\n const [,triggerRender] = useState()\r\n let state = 'hello'\r\n\r\n function handleButtonClick() {\r\n state = 'hi'\r\n triggerRender()\r\n }\r\n return (\r\n <>\r\n

{state}

\r\n \r\n \r\n )\r\n}\r\n```\r\nuseState 반환값의 두 번째 원소를 실행해 리액트에 렌더링이 일어나게끔 변경했다. \r\n그럼에도 여전히 버튼 클릭시 state의 변경된 값이 렌더링되고 있지 않다. \r\n\r\n그 이유는 리액트의 렌더링은 **함수 컴포넌트에서 반환한 결과물인 return의 값을 비교해 실행되기 때문이다.** \r\n\r\n즉, 매번 렌더링이 발생할 때마다 함수는 새롭게 실행이 되고, 실행한 함수에서 state는 매번 hello로 초기화 되므로 아무리 state를 변경해도 hello로 초기화 되는 것이다. \r\n\r\n근데 렌더링이 될 때마다 초기화되는 변수(값)와는 달리, useState의 결과값은 어떻게 그 값을 유지할까?\r\n\r\n\r\n\r\n그럼 우리가 알고있는 useState는 대체 어떻게 구현이 되있는 것일지 한번 최대한 비슷하게 구현한 코드를 살펴보자.\r\n\r\n**먼저 useState의 결과 값이 유지되도록, state를 함수로 하여 state 값을 호출할 때마다 현재 state를 반환하게 해보자.**\r\n```js\r\nfunction useState(initialValue) {\r\n\tlet initialState = initialValue;\r\n\t\r\n\tfunction state() {\r\n\t\treturn initialState\r\n\t}\r\n\tfunction setState(newValue) {\r\n\t\tinitialState = newValue\r\n\t}\r\n\treturn [state, setState];\r\n}\r\nconst [value, setState] = useState(0);\r\nsetValue(1);\r\nconsole.log(value()); // 1\r\n```\r\n위의 코드도 나쁘진 않지만, 우리에게 익숙한 useState훅은 state를 함수가 아닌 상수처럼 사용하고 있다.\r\n어떻게 그게 가능한 걸까?\r\n\r\n## 클로저를 이용해 상태를 관리하는 useState\r\n이를 위해서 리액트는 **클로저**를 이용한 것이다. \r\nuseState는 클로저를 통해 useState 내부의 선언된 함수(setState)가 함수의 실행이 종료된 이후(useState가 호출된 이후)에도 지역변수인 state를 계속 참조할 수 있다.\r\n\r\nuseState 작동 방식을 대략적으로 흉내 낸 코드는 다음과 같다.\r\n\r\n```js\r\nconst MyReact = (function() {\r\n\tconst global = {}\r\n let index = 0\r\n \r\n function useState(initialState){\r\n \tif(!global.states) {\r\n \t// 애플리케이션 전체의 states 배열 초기화, 최초 접근이면 빈 배열로\r\n \tglobal.states = []\r\n }\r\n // states 정보를 조회해서, 현재 상태값이 있는지 확인\r\n // 없다면 초깃값으로 설정\r\n const currentState = global.states[index] || initialState\r\n // 위에서 조회한 값으로 states의 값 업데이트\r\n global.states[index] = currentState\r\n \r\n // 즉시실행함수로 setter 만듬\r\n const setState = (function() {\r\n \t// 클로저로 index를 가둬두어서 동일한 index에 접근이 가능\r\n \tlet currentIndex = index\r\n return function(value){\r\n \tglobal.states[currentIndex] = value\r\n //컴포넌트 렌더링이 들어가는 부분이다.(실제 코드는 생략)\r\n }\r\n })()\r\n // useState를 쓸 때마다 index를 하나씩 추가하는데, 이는 하나의 state마다\r\n // index가 할당되어있어, 그 index가 배열의 값(global.states)를 가리키고,\r\n // 필요할 때마다 그 값을 가져오게 하는 것이다.\r\n index = index + 1\r\n \r\n return [currentState,setState]\r\n}\r\n\r\nfunction Component() {\r\n\tconst [value, setValue] = useState(0);\r\n}\r\n})();\r\n```\r\n\r\n실제 리액트 코드에서는 useReducer를 이용해 구현되어 있어 약간의 차이가 있다.\r\n\r\n아무튼 여기서 함수의 실행이 끝났음에도 함수가 선언된 환경을 기억할 수 있는 방법이 바로 클로저인 것이다. 만약 클로저가 없다면, `setState`는 항상 `index`의 현재 값에 의존하게 된다. 즉, 컴포넌트가 여러 상태를 갖고 있을 때 마지막 `index`만 참조하므로, `setState`가 올바른 위치를 참조하지 않게 되는 것이다.\r\n\r\n매번 실행되는 함수 컴포넌트 환경에서 state의 값을 유지하고 사용하기 위해 리액트는 클로저를 활용하고 있다.\r\n\r\n`\r\n훅에 대한 구현체를 github에서 타고 올라가다보면 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 라는 문구를 만나게된다(무섭다 ㅋㅋ)\r\n위의 코드는 Preact의 구현을 기준으로 하고 있다. Preact는 react의 경량화 버전으로,\r\n대부분의 리액트 API를 지원하고 있다. \r\n`\r\n## 결론\r\nReact의 `useState`는 클로저를 통해 상태값을 안정적으로 유지하며, 함수 컴포넌트가 여러 번 호출되더라도 **각 상태값이 고유한 위치에 저장**될 수 있게 한다. `useState`가 반환하는 `setState` 함수는 생성 당시의 상태 위치(`index`)를 클로저로 캡처하여, 해당 상태값만 정확히 업데이트하도록 구현되어 있다.\r\n\r\n정리하자면, 클로저는 `setState`가 함수가 선언된 당시의 환경을 유지하게 해주기 때문에 **컴포넌트가 매번 재실행될 때마다 상태가 초기화되는 것을 방지**하고, 상태가 올바르게 유지될 수 있게 해준다.\r\n\r\n**참고**\r\n\r\n[서적] 모던 리액트 Deep Dive\r\n"}]},"__N_SSG":true} \ No newline at end of file diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.json" "b/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.json" deleted file mode 100644 index 8b4f1fd..0000000 --- "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.json" +++ /dev/null @@ -1 +0,0 @@ -{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs","all"],"currentTag":"이분탐색","posts":[{"slug":"posts/Algorithm/[PCCP 기출문제]퍼즐게임 챌린지","title":"[PCCP 기출문제] 퍼즐게임 챌린지","date":"2024-10-31T12:05:24.000Z","image":"index.jpg","summary":"프로그래머스 [PCCP 기출문제] level 2 퍼즐게임 챌린지 문제 풀이 입니다.","tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색"],"content":"# [PCCP 기출문제] 퍼즐게임 챌린지\r\n\r\n## 문제 요약\r\nhttps://school.programmers.co.kr/learn/courses/30/lessons/340212\r\n\r\n숙련도에 따라 퍼즐을 풀 때, \r\n- 각 퍼즐에서 난이도가 숙련도 보다 높다면 **난이도 - 숙련도 만큼 퍼즐이 틀리고, 틀린횟수 * (이전시간 + 현재시간)만큼의 시간이 걸린다.**\r\n- 각 퍼즐에서 난이도가 숙련도 보다 낮거나 같다면 **현재시간만** 투자하면 된다.\r\n\r\n이렇게 모든 퍼즐을 푼 시간의 합이 주어진 limit를 넘지 않도록 하는 숙련도(level)의 최솟값을 구하여야한다. \r\n\r\n(자세한 문제의 내용은 링크를 참고바랍니다.)\r\n\r\n## 문제 접근\r\n레벨의 최솟값을 구하여야 하기에, 처음에는 diffs 난이도 배열에서 최댓값부터 시작하여 1씩 줄어들도록 푸는 브루트포스를 생각했다.\r\n\r\n하지만, 제한사항이 1<=diffs[i]<=100000로 범위가 상당히 크고, diffs의 길이도 1 ≤ diffs의 길이 = times의 길이 = n ≤ 300,000로 제한사항이 주어졌기에\r\n\r\n브루트포스 기법은 무조건 시간초과가 날 것이므로 아닌 다른 방식을 생각해내야한다. \r\n\r\n결국 level의 값을 효율적이고 시간이 적게 들 수 있게 탐색해내야 하므로, O(nlogn)의 시간 복잡도를 가지는 **이분탐색으로 level의 최솟값을 구하면 된다.**\r\n\r\n## 풀이\r\n최대 난이도 이상의 값을 탐색하는 것은 의미가 없으므로, \r\n난이도의 최댓값을 구하여, max_diff로 놓은다음 이를 초기에 r로 설정한다.\r\nl은 난이도의 최솟값인 1로 두고,\r\n\r\n1~max_diff 범위 안에서 레벨의 이분탐색을 진행한다.\r\n\r\n현재 레벨을 변수 `level`로 둘 때,\r\n\r\n각 `level`에서 나올 수 있는 경우는 퍼즐을 푸는 시간의 합이 `limit`보다 **높을 때**와 **낮거나 같을 때**로 나눌 수 있다.\r\n- 시간의 합이 `limit`보다 높을 때: 현재 레벨이 낮기에 그런 것 이므로, 더 높은 레벨을 탐색하기 위해 \r\n`l = level+1` 로 둔다.\r\n\r\n- 시간의 합이 `limit` 보다 낮거나 같을 때: 현재 레벨이 충분히 높기에, 우리는 레벨의 최솟값을 찾아내야 하므로, \r\n`r = level-1`로 두고, 현재 레벨이 답이 될 수도 있기에 `answer = level` 도 추가해야 한다.\r\n\r\n이를 전체 코드로 구현하면 다음과 같다.\r\n\r\n## 전체 코드\r\n```js\r\n def solution(diffs, times, limit):\r\n max_diff = max(diffs)\r\n l = 1\r\n r = max_diff;\r\n answer = max_diff;\r\n while l 0:\r\n time += w_count*(times[i-1] + times[i]) + times[i];\r\n else:\r\n time += times[i];\r\n if time>limit:\r\n l = level+1;\r\n else:\r\n r = level;\r\n answer = level;\r\n return answer;\r\n ```\r\n\r\n## 느낀 점\r\n한 동안 알고리즘 문제를 안풀었더니 뇌가 굳은게 아닌가 싶다. 그리고 문제 설명 자체가 길고 복잡하면 살짝 길을 잃는 습관이 있는데, 이번 문제에서도 이해해보면 그렇게 어렵지 않은 것 같음에도 불구하고 그런 습관이 나타난 것 같다. \r\n\r\n전에 백준에서 풀었던 이분탐색문제랑 거의 흡사했음에도 이분탐색을 빨리 떠올리지 못한게 아쉽다."},{"slug":"posts/Algorithm/[프로그래머스lv3]징검다리건너기","title":"[프로그래머스 level 3] 징검다리 건너기","date":"2024-07-08T18:33:24.000Z","image":"index.png","summary":"이분탐색을 활용한 프로그래머스 [level 3] 징검다리 건너기 문제 풀이 입니다.","tags":["Algorithm","프로그래머스","lv3","이분탐색"],"content":"# [level 3] 징검다리 건너기 - 64062\r\n\r\n## 문제 설명\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/64062.%E2%80%85%EC%A7%95%EA%B2%80%EB%8B%A4%EB%A6%AC%E2%80%85%EA%B1%B4%EB%84%88%EA%B8%B0/README.md#%EB%AC%B8%EC%A0%9C-%EC%84%A4%EB%AA%85)\r\n\r\n**[본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.]**\r\n\r\n카카오 초등학교의 \"니니즈 친구들\"이 \"라이언\" 선생님과 함께 가을 소풍을 가는 중에 **징검다리**가 있는 개울을 만나서 건너편으로 건너려고 합니다. \"라이언\" 선생님은 \"니니즈 친구들\"이 무사히 징검다리를 건널 수 있도록 다음과 같이 규칙을 만들었습니다.\r\n\r\n- 징검다리는 일렬로 놓여 있고 각 징검다리의 디딤돌에는 모두 숫자가 적혀 있으며 디딤돌의 숫자는 한 번 밟을 때마다 1씩 줄어듭니다.\r\n- 디딤돌의 숫자가 0이 되면 더 이상 밟을 수 없으며 이때는 그 다음 디딤돌로 한번에 여러 칸을 건너 뛸 수 있습니다.\r\n- 단, 다음으로 밟을 수 있는 디딤돌이 여러 개인 경우 무조건 가장 가까운 디딤돌로만 건너뛸 수 있습니다.\r\n\r\n\"니니즈 친구들\"은 개울의 왼쪽에 있으며, 개울의 오른쪽 건너편에 도착해야 징검다리를 건넌 것으로 인정합니다. \r\n\"니니즈 친구들\"은 한 번에 한 명씩 징검다리를 건너야 하며, 한 친구가 징검다리를 모두 건넌 후에 그 다음 친구가 건너기 시작합니다.\r\n\r\n디딤돌에 적힌 숫자가 순서대로 담긴 배열 stones와 한 번에 건너뛸 수 있는 디딤돌의 최대 칸수 k가 매개변수로 주어질 때, 최대 몇 명까지 징검다리를 건널 수 있는지 return 하도록 solution 함수를 완성해주세요.\r\n\r\n#### [제한사항]\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/64062.%E2%80%85%EC%A7%95%EA%B2%80%EB%8B%A4%EB%A6%AC%E2%80%85%EA%B1%B4%EB%84%88%EA%B8%B0/README.md#%EC%A0%9C%ED%95%9C%EC%82%AC%ED%95%AD)\r\n\r\n- 징검다리를 건너야 하는 니니즈 친구들의 수는 무제한 이라고 간주합니다.\r\n- stones 배열의 크기는 1 이상 200,000 이하입니다.\r\n- stones 배열 각 원소들의 값은 1 이상 200,000,000 이하인 자연수입니다.\r\n- k는 1 이상 stones의 길이 이하인 자연수입니다.\r\n\r\n----------\r\n\r\n#### [입출력 예]\r\n\r\n\r\n\r\n \r\n\r\n\r\n\r\n\r\n\r\n \r\n\r\n\r\n\r\n\r\n\r\n
stoneskresult
[2, 4, 5, 3, 2, 1, 4, 2, 5, 1]33
\r\n\r\n##### **입출력 예에 대한 설명**\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/64062.%E2%80%85%EC%A7%95%EA%B2%80%EB%8B%A4%EB%A6%AC%E2%80%85%EA%B1%B4%EB%84%88%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88%EC%97%90-%EB%8C%80%ED%95%9C-%EC%84%A4%EB%AA%85)\r\n\r\n----------\r\n\r\n**입출력 예 #1**\r\n\r\n첫 번째 친구는 다음과 같이 징검다리를 건널 수 있습니다. \r\n![1.png](1.png)\r\n\r\n첫 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n두 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다. \r\n![2.png](2.png)\r\n\r\n두 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n세 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다. \r\n![3.png](3.png)\r\n\r\n세 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n네 번째 친구가 징검다리를 건너려면, 세 번째 디딤돌에서 일곱 번째 디딤돌로 네 칸을 건너뛰어야 합니다. 하지만 k = 3 이므로 건너뛸 수 없습니다. \r\n![4.png](4.png)\r\n\r\n따라서 최대 3명이 디딤돌을 모두 건널 수 있습니다.\r\n\r\n## 접근 방법\r\n브루트 포스로 1명씩 건너기에는, stones 배열의 크기가 최대 200,000이고, stones 배열의 각 원소들의 값 역시 최대 200,000,000이기 때문에, 시간 초과가 날 확률이 높다고 생각했고(사실 그렇게 쉬우면 lv3 일리가 없지), 아니나 다를까 시간초과가 났다. \r\n\r\n고민하다가 도저히 해결책이 안나와서 찾아보니 `이분탐색`을 사용하여 풀 수 있었다.\r\n\r\n- 건널 수 있는 인원의 최소는 1명, 최대는 max(stones)명이다. 모든 돌들의 합이 같거나, 다르다 하더라도 건널수 없는 곳이 k이하면 건널 수 있기 때문이다. \r\n- 따라서 start = 1 end = max(stones)로 이분탐색을 시작할 수 있다.\r\n\r\nmid 값만큼의 사람이 건널 수 있는지 확인하기 위한 check함수는 다음과 같다.\r\n```python\r\ndef check(stones, mid,k):\r\n cnt = 0;\r\n for stone in stones:\r\n\t # 밟을 수 없으므로 건너뛰어야하는 횟수를 늘린다.\r\n if (stone - mid) <= 0:\r\n cnt += 1;\r\n # 밟을 수 있으므로 최대 건너뛰는 횟수를 초기화시킨다.\r\n else:\r\n cnt = 0;\r\n # 건너뛰는 횟수가 k보다 크거나 같으면 안되므로 False 리턴 \r\n if cnt>=k:\r\n return False;\r\n return True;\r\n```\r\n건널 수 있다면 mid보다 큰 범위에서 값을 찾아야하므로 start를 mid+1로 갱신하고, 반대라면, mid보다 작은 범위에서 값을 찾아야하므로 end를 mid-1로 갱신한다.\r\n \r\ncheck 시간 복잡도는 O(N)이고 이분탐색을 했을 때 O(logN)의 시간복잡도가 나오므로, 총 O(NlogN)의 시간 복잡도가 나오므로, 시간초과가 나지 않는다.\r\n\r\n전체 코드는 다음과 같다.\r\n```python\r\ndef check(stones, mid,k):\r\n cnt = 0;\r\n for stone in stones:\r\n if (stone - mid) <= 0:\r\n cnt += 1;\r\n else:\r\n cnt = 0;\r\n if cnt>=k:\r\n return False;\r\n return True;\r\n\r\ndef solution(stones, k):\r\n start,end = 1,max(stones);\r\n answer = 0;\r\n while start<=end:\r\n mid = (start + end) // 2;\r\n if check(stones, mid,k):\r\n start = mid+1;\r\n else:\r\n answer = mid;\r\n end = mid-1; \r\n return answer;\r\n```\r\n\r\n## 느낀 점\r\n이분탐색 문제를 많이는 아니더라도 어느정도는 풀어봤다고 생각했는데, 이 문제에서 이분탐색으로 푸는 아이디어를 떠올리지 못해서 아쉽다. \r\n\r\n또한 처음에 위에 코드의 6번째줄에서 else문을 쓰지않고, 이러한 코드로 제출하여서 시간초과가 났다.\r\n```python\r\nif (stone - mid) <= 0:\r\n\tcnt += 1;\r\nif (stone - mid)>0:\r\n\tcnt = 0;\r\n```\r\nㅎㅎ.. 사실 위와 같은 경우에서 if else문을 쓰는 것이 기본인데, 그래도 시간초과가 날 수 있는 직접적인 원인까지는 되지 않을 거라고 안일하게 생각해서 코드를 짠 게 화근이었다. 기본을 지키자!"}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/index.json b/_next/data/pwbOjWHyyP97zls4vVRix/index.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/index.json rename to _next/data/pwbOjWHyyP97zls4vVRix/index.json diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm.json diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm/[PCCP \352\270\260\354\266\234\353\254\270\354\240\234]\355\215\274\354\246\220\352\262\214\354\236\204 \354\261\214\353\246\260\354\247\200.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm/[PCCP \352\270\260\354\266\234\353\254\270\354\240\234]\355\215\274\354\246\220\352\262\214\354\236\204 \354\261\214\353\246\260\354\247\200.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm/[PCCP \352\270\260\354\266\234\353\254\270\354\240\234]\355\215\274\354\246\220\352\262\214\354\236\204 \354\261\214\353\246\260\354\247\200.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm/[PCCP \352\270\260\354\266\234\353\254\270\354\240\234]\355\215\274\354\246\220\352\262\214\354\236\204 \354\261\214\353\246\260\354\247\200.json" diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm/[\353\260\261\354\244\200 gold 5] \353\271\214\353\237\260 \355\230\270\354\204\235.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm/[\353\260\261\354\244\200 gold 5] \353\271\214\353\237\260 \355\230\270\354\204\235.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm/[\353\260\261\354\244\200 gold 5] \353\271\214\353\237\260 \355\230\270\354\204\235.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm/[\353\260\261\354\244\200 gold 5] \353\271\214\353\237\260 \355\230\270\354\204\235.json" diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\225\204\354\235\264\355\205\234 \354\244\215\352\270\260.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\225\204\354\235\264\355\205\234 \354\244\215\352\270\260.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\225\204\354\235\264\355\205\234 \354\244\215\352\270\260.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\225\204\354\235\264\355\205\234 \354\244\215\352\270\260.json" diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\247\225\352\262\200\353\213\244\353\246\254\352\261\264\353\204\210\352\270\260.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\247\225\352\262\200\353\213\244\353\246\254\352\261\264\353\204\210\352\270\260.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\247\225\352\262\200\353\213\244\353\246\254\352\261\264\353\204\210\352\270\260.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\247\225\352\262\200\353\213\244\353\246\254\352\261\264\353\204\210\352\270\260.json" diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript.json diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript/javascriptdeepdive09.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript/javascriptdeepdive09.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript/javascriptdeepdive09.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript/javascriptdeepdive09.json diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript/javascriptdeepdive10.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript/javascriptdeepdive10.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript/javascriptdeepdive10.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript/javascriptdeepdive10.json diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript/javascriptdeepdive11.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript/javascriptdeepdive11.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript/javascriptdeepdive11.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript/javascriptdeepdive11.json diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript/\355\201\264\353\241\234\354\240\200\354\227\220 \353\214\200\355\225\230\354\227\254.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript/\355\201\264\353\241\234\354\240\200\354\227\220 \353\214\200\355\225\230\354\227\254.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/Javascript/\355\201\264\353\241\234\354\240\200\354\227\220 \353\214\200\355\225\230\354\227\254.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/Javascript/\355\201\264\353\241\234\354\240\200\354\227\220 \353\214\200\355\225\230\354\227\254.json" diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/React.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/React.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/React.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/React.json diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/React/React useState \355\233\205\352\263\274 \355\201\264\353\241\234\354\240\200.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/React/React useState \355\233\205\352\263\274 \355\201\264\353\241\234\354\240\200.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/React/React useState \355\233\205\352\263\274 \355\201\264\353\241\234\354\240\200.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/React/React useState \355\233\205\352\263\274 \355\201\264\353\241\234\354\240\200.json" diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/React/[React DeepDive] React\354\235\230 \352\260\200\354\203\201 DOM.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/React/[React DeepDive] React\354\235\230 \352\260\200\354\203\201 DOM.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/React/[React DeepDive] React\354\235\230 \352\260\200\354\203\201 DOM.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/React/[React DeepDive] React\354\235\230 \352\260\200\354\203\201 DOM.json" diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/all.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/all.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/all.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/all.json diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/search.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/search.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/search.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/search.json diff --git a/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/Algorithm.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/Algorithm.json new file mode 100644 index 0000000..2cfba08 --- /dev/null +++ b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/Algorithm.json @@ -0,0 +1 @@ +{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs","all"],"currentTag":"Algorithm","posts":[{"slug":"posts/Algorithm/[PCCP 기출문제]퍼즐게임 챌린지","title":"[PCCP 기출문제] 퍼즐게임 챌린지","date":"2024-10-31T12:05:24.000Z","image":"index.jpg","summary":"프로그래머스 [PCCP 기출문제] level 2 퍼즐게임 챌린지 문제 풀이 입니다.","tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색"],"content":"# [PCCP 기출문제] 퍼즐게임 챌린지\r\n\r\n## 문제 요약\r\nhttps://school.programmers.co.kr/learn/courses/30/lessons/340212\r\n\r\n숙련도에 따라 퍼즐을 풀 때, \r\n- 각 퍼즐에서 난이도가 숙련도 보다 높다면 **난이도 - 숙련도 만큼 퍼즐이 틀리고, 틀린횟수 * (이전시간 + 현재시간)만큼의 시간이 걸린다.**\r\n- 각 퍼즐에서 난이도가 숙련도 보다 낮거나 같다면 **현재시간만** 투자하면 된다.\r\n\r\n이렇게 모든 퍼즐을 푼 시간의 합이 주어진 limit를 넘지 않도록 하는 숙련도(level)의 최솟값을 구하여야한다. \r\n\r\n(자세한 문제의 내용은 링크를 참고바랍니다.)\r\n\r\n## 문제 접근\r\n레벨의 최솟값을 구하여야 하기에, 처음에는 diffs 난이도 배열에서 최댓값부터 시작하여 1씩 줄어들도록 푸는 브루트포스를 생각했다.\r\n\r\n하지만, 제한사항이 1<=diffs[i]<=100000로 범위가 상당히 크고, diffs의 길이도 1 ≤ diffs의 길이 = times의 길이 = n ≤ 300,000로 제한사항이 주어졌기에\r\n\r\n브루트포스 기법은 무조건 시간초과가 날 것이므로 아닌 다른 방식을 생각해내야한다. \r\n\r\n결국 level의 값을 효율적이고 시간이 적게 들 수 있게 탐색해내야 하므로, O(nlogn)의 시간 복잡도를 가지는 **이분탐색으로 level의 최솟값을 구하면 된다.**\r\n\r\n## 풀이\r\n최대 난이도 이상의 값을 탐색하는 것은 의미가 없으므로, \r\n난이도의 최댓값을 구하여, max_diff로 놓은다음 이를 초기에 r로 설정한다.\r\nl은 난이도의 최솟값인 1로 두고,\r\n\r\n1~max_diff 범위 안에서 레벨의 이분탐색을 진행한다.\r\n\r\n현재 레벨을 변수 `level`로 둘 때,\r\n\r\n각 `level`에서 나올 수 있는 경우는 퍼즐을 푸는 시간의 합이 `limit`보다 **높을 때**와 **낮거나 같을 때**로 나눌 수 있다.\r\n- 시간의 합이 `limit`보다 높을 때: 현재 레벨이 낮기에 그런 것 이므로, 더 높은 레벨을 탐색하기 위해 \r\n`l = level+1` 로 둔다.\r\n\r\n- 시간의 합이 `limit` 보다 낮거나 같을 때: 현재 레벨이 충분히 높기에, 우리는 레벨의 최솟값을 찾아내야 하므로, \r\n`r = level-1`로 두고, 현재 레벨이 답이 될 수도 있기에 `answer = level` 도 추가해야 한다.\r\n\r\n이를 전체 코드로 구현하면 다음과 같다.\r\n\r\n## 전체 코드\r\n```js\r\n def solution(diffs, times, limit):\r\n max_diff = max(diffs)\r\n l = 1\r\n r = max_diff;\r\n answer = max_diff;\r\n while l 0:\r\n time += w_count*(times[i-1] + times[i]) + times[i];\r\n else:\r\n time += times[i];\r\n if time>limit:\r\n l = level+1;\r\n else:\r\n r = level;\r\n answer = level;\r\n return answer;\r\n ```\r\n\r\n## 느낀 점\r\n한 동안 알고리즘 문제를 안풀었더니 뇌가 굳은게 아닌가 싶다. 그리고 문제 설명 자체가 길고 복잡하면 살짝 길을 잃는 습관이 있는데, 이번 문제에서도 이해해보면 그렇게 어렵지 않은 것 같음에도 불구하고 그런 습관이 나타난 것 같다. \r\n\r\n전에 백준에서 풀었던 이분탐색문제랑 거의 흡사했음에도 이분탐색을 빨리 떠올리지 못한게 아쉽다."},{"slug":"posts/Algorithm/[백준 gold 5] 빌런 호석","title":"[백준 gold 5] 빌런 호석","date":"2024-08-09T17:05:24.000Z","image":"index.png","summary":"dfs을 활용한 프로그래머스 [level 3] 징검다리 건너기 문제 풀이 입니다.","tags":["Algorithm","백준","gold","dfs"],"content":"# [gold 3] 빌런 호석\r\n\r\n## 문제 설명\r\n\r\n치르보기 빌딩은 11층부터 NN층까지 이용이 가능한 엘리베이터가 있다. 엘리베이터의 층수를 보여주는 디스플레이에는 KK 자리의 수가 보인다. 수는 00으로 시작할 수도 있다. 00부터 99까지의 각 숫자가 디스플레이에 보이는 방식은 아래와 같다. 각 숫자는 7개의 표시등 중의 일부에 불이 들어오면서 표현된다.\r\n\r\n![](1.png)\r\n\r\n예를 들어 K=4K=4인 경우에 16801680층과 501501층은 아래와 같이 보인다.\r\n\r\n![](2.png)\r\n\r\n\r\n\r\n빌런 호석은 치르보기 빌딩의 엘리베이터 디스플레이의 LED 중에서 최소 11개, 최대 PP개를 반전시킬 계획을 세우고 있다. 반전이란 켜진 부분은 끄고, 꺼진 부분은 켜는 것을 의미한다. 예를 들어 숫자 11을 22로 바꾸려면 총 5개의 LED를 반전시켜야 한다. 또한 반전 이후에 디스플레이에 올바른 수가 보여지면서 11 이상 NN 이하가 되도록 바꿔서 사람들을 헷갈리게 할 예정이다. 치르보기를 사랑하는 모임의 회원인 당신은 호석 빌런의 행동을 미리 파악해서 혼쭐을 내주고자 한다. 현재 엘리베이터가 실제로는 XX층에 멈춰있을 때, 호석이가 반전시킬 LED를 고를 수 있는 경우의 수를 계산해보자.\r\n\r\n### 입력\r\n\r\n N,K,P,XN,K,P,X 가 공백으로 구분되어 첫째 줄에 주어진다.\r\n\r\n### 출력\r\n\r\n호석 빌런이 엘리베이터 LED를 올바르게 반전시킬 수 있는 경우의 수를 계산해보자.\r\n\r\n## 접근 방법\r\n\r\n숫자에 따른 LED를 먼저 구현하기 위해, \r\n\r\n그림과 같이, 각 LED의 위치에 번호를 매겨, 배열안에 그 번호가 있으면 불이 켜져있는 것으로, 없다면 불이 꺼져 있는 것으로 표현하였다.\r\n\"이미지\r\n\r\n\r\n그림처럼 0을 표현하면 3번자리를 제외한 모든 번호가 켜져있으므로, `[0,1,2,4,5,6]`으로 표현 할 수 있다.\r\n\r\n각 번호를 이와 같이 바꾸어 numbers 배열에 저장하면, 이와 같다.\r\n```python\r\nnumbers = [[0,1,2,4,5,6],[2,5],[0,2,3,4,6],[0,2,3,5,6],[1,2,3,5],[0,1,3,5,6],[0,1,3,4,5,6],[0,2,5],[0,1,2,3,4,5,6],[0,1,2,3,5,6]];\r\n```\r\n이후 만일 현재 번호와 비교할 번호의 LED를 비교하기 위해 서로의 차집합을 더해서 두 번호가 서로를 비교했을 때, 가지고 있는 혹은 없는 번호의 개수를 구한다.\r\n```python\r\ncur_count = len(list(cur.difference(compare))) + len(list(compare.difference(cur)));\r\n```\r\n이와 같은 방식으로 반전시킬 LED의 개수를 구하고, dfs를 활용하여 x의 모든 자릿수를 순회하여 p보다 작거나 같게 LED를 반전하면서, n보다 번호가 작거나 같다면 result를 1 증가 시킨다.\r\n\r\n전체코드는 다음과 같다.\r\n```python\r\nimport sys;\r\ninput = sys.stdin.readline;\r\n\r\nn,k,p,x = map(int, input().split());\r\nresult = 0;\r\nnumbers = [[0,1,2,4,5,6],[2,5],[0,2,3,4,6],[0,2,3,5,6],[1,2,3,5],[0,1,3,5,6],[0,1,3,4,5,6],[0,2,5],[0,1,2,3,4,5,6],[0,1,2,3,5,6]];\r\n\r\nx = list(str(x));\r\nif len(x) != k:\r\n\t# k 보다 자릿수 x가 자릿수가 작다면 작은 만큼 앞에 0을 채워주어야 한다.\r\n for _ in range(k-len(x)):\r\n x.insert(0,'0');\r\n \r\ndef dfs(index,count,st):\r\n global result;\r\n if count>p:\r\n return;\r\n if index == k:\r\n if 0\r\n \r\nrectangle\r\ncharacterX\r\ncharacterY\r\nitemX\r\nitemY\r\nresult\r\n\r\n\r\n \r\n[[1,1,7,4],[3,2,5,5],[4,3,6,9],[2,6,8,8]]\r\n1\r\n3\r\n7\r\n8\r\n17\r\n\r\n\r\n[[1,1,8,4],[2,2,4,9],[3,6,9,8],[6,3,7,7]]\r\n9\r\n7\r\n6\r\n1\r\n11\r\n\r\n\r\n[[1,1,5,7]]\r\n1\r\n1\r\n4\r\n7\r\n9\r\n\r\n\r\n[[2,1,7,5],[6,4,10,10]]\r\n3\r\n1\r\n7\r\n10\r\n15\r\n\r\n\r\n[[2,2,5,5],[1,3,6,4],[3,1,4,6]]\r\n1\r\n4\r\n6\r\n3\r\n10\r\n\r\n\r\n \r\n\r\n##### 입출력 예 설명\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88-%EC%84%A4%EB%AA%85)\r\n\r\n입출력 예 #1\r\n\r\n![rect_5.png](6.png)\r\n\r\n캐릭터 위치는 (1, 3)이며, 아이템 위치는 (7, 8)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n입출력 예 #2\r\n\r\n![rect_7.png](7.png)\r\n\r\n캐릭터 위치는 (9, 7)이며, 아이템 위치는 (6, 1)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n입출력 예 #3\r\n\r\n[![rect_8.png](8.png)]\r\n\r\n캐릭터 위치는 (1, 1)이며, 아이템 위치는 (4, 7)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n## 구현 방법\r\n### 전체 직사각형들을 합친 것의 바깥쪽 테두리만 돌게하려면 어떻게 하는가?\r\n- rectangle을 순회하면서 범위 중 직사각형 내부는 0으로 테두리는 1로 board값을 갱신한다.\r\n- 이 때, 만일 `다른 직사각형의 내부안에 현재 직사각형의 테두리가 있지 않을 경우`에만 board를 1로 갱신한다. \r\n### 예외 사항\r\n![exception.png](exception.png)\r\n좌표가 인접한 경우 위와 같이 의도하지 않은 경우를 초래할 수도 있다.\r\n\r\n따라서 이를 해결하기 위해 모든 좌표의 값을 **2배**씩 해주어야 한다.\r\n\r\n이를 모두 반영하여 board 테이블을 만들면,\r\n\r\n```python\r\nboard = [[-1 for _ in range(102)] for _ in range(102)];\r\nfor r in rectangle:\r\n\t# map 객체를 통해 모든 좌표값에 2배\r\n\tx1,y1,x2,y2 = map(lambda x: x*2,r);\r\n for i in range(x1,x2+1):\r\n for j in range(y1,y2+1):\r\n\t # 직사각형 내부의 경우 0으로 개신\r\n if x1\r\n \r\nstones\r\nk\r\nresult\r\n\r\n\r\n \r\n[2, 4, 5, 3, 2, 1, 4, 2, 5, 1]\r\n3\r\n3\r\n\r\n\r\n \r\n\r\n##### **입출력 예에 대한 설명**\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/64062.%E2%80%85%EC%A7%95%EA%B2%80%EB%8B%A4%EB%A6%AC%E2%80%85%EA%B1%B4%EB%84%88%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88%EC%97%90-%EB%8C%80%ED%95%9C-%EC%84%A4%EB%AA%85)\r\n\r\n----------\r\n\r\n**입출력 예 #1**\r\n\r\n첫 번째 친구는 다음과 같이 징검다리를 건널 수 있습니다. \r\n![1.png](1.png)\r\n\r\n첫 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n두 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다. \r\n![2.png](2.png)\r\n\r\n두 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n세 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다. \r\n![3.png](3.png)\r\n\r\n세 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n네 번째 친구가 징검다리를 건너려면, 세 번째 디딤돌에서 일곱 번째 디딤돌로 네 칸을 건너뛰어야 합니다. 하지만 k = 3 이므로 건너뛸 수 없습니다. \r\n![4.png](4.png)\r\n\r\n따라서 최대 3명이 디딤돌을 모두 건널 수 있습니다.\r\n\r\n## 접근 방법\r\n브루트 포스로 1명씩 건너기에는, stones 배열의 크기가 최대 200,000이고, stones 배열의 각 원소들의 값 역시 최대 200,000,000이기 때문에, 시간 초과가 날 확률이 높다고 생각했고(사실 그렇게 쉬우면 lv3 일리가 없지), 아니나 다를까 시간초과가 났다. \r\n\r\n고민하다가 도저히 해결책이 안나와서 찾아보니 `이분탐색`을 사용하여 풀 수 있었다.\r\n\r\n- 건널 수 있는 인원의 최소는 1명, 최대는 max(stones)명이다. 모든 돌들의 합이 같거나, 다르다 하더라도 건널수 없는 곳이 k이하면 건널 수 있기 때문이다. \r\n- 따라서 start = 1 end = max(stones)로 이분탐색을 시작할 수 있다.\r\n\r\nmid 값만큼의 사람이 건널 수 있는지 확인하기 위한 check함수는 다음과 같다.\r\n```python\r\ndef check(stones, mid,k):\r\n cnt = 0;\r\n for stone in stones:\r\n\t # 밟을 수 없으므로 건너뛰어야하는 횟수를 늘린다.\r\n if (stone - mid) <= 0:\r\n cnt += 1;\r\n # 밟을 수 있으므로 최대 건너뛰는 횟수를 초기화시킨다.\r\n else:\r\n cnt = 0;\r\n # 건너뛰는 횟수가 k보다 크거나 같으면 안되므로 False 리턴 \r\n if cnt>=k:\r\n return False;\r\n return True;\r\n```\r\n건널 수 있다면 mid보다 큰 범위에서 값을 찾아야하므로 start를 mid+1로 갱신하고, 반대라면, mid보다 작은 범위에서 값을 찾아야하므로 end를 mid-1로 갱신한다.\r\n \r\ncheck 시간 복잡도는 O(N)이고 이분탐색을 했을 때 O(logN)의 시간복잡도가 나오므로, 총 O(NlogN)의 시간 복잡도가 나오므로, 시간초과가 나지 않는다.\r\n\r\n전체 코드는 다음과 같다.\r\n```python\r\ndef check(stones, mid,k):\r\n cnt = 0;\r\n for stone in stones:\r\n if (stone - mid) <= 0:\r\n cnt += 1;\r\n else:\r\n cnt = 0;\r\n if cnt>=k:\r\n return False;\r\n return True;\r\n\r\ndef solution(stones, k):\r\n start,end = 1,max(stones);\r\n answer = 0;\r\n while start<=end:\r\n mid = (start + end) // 2;\r\n if check(stones, mid,k):\r\n start = mid+1;\r\n else:\r\n answer = mid;\r\n end = mid-1; \r\n return answer;\r\n```\r\n\r\n## 느낀 점\r\n이분탐색 문제를 많이는 아니더라도 어느정도는 풀어봤다고 생각했는데, 이 문제에서 이분탐색으로 푸는 아이디어를 떠올리지 못해서 아쉽다. \r\n\r\n또한 처음에 위에 코드의 6번째줄에서 else문을 쓰지않고, 이러한 코드로 제출하여서 시간초과가 났다.\r\n```python\r\nif (stone - mid) <= 0:\r\n\tcnt += 1;\r\nif (stone - mid)>0:\r\n\tcnt = 0;\r\n```\r\nㅎㅎ.. 사실 위와 같은 경우에서 if else문을 쓰는 것이 기본인데, 그래도 시간초과가 날 수 있는 직접적인 원인까지는 되지 않을 거라고 안일하게 생각해서 코드를 짠 게 화근이었다. 기본을 지키자!"}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/JavaScript.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/JavaScript.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/JavaScript.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/tag/JavaScript.json diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/PCCP.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/PCCP.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/PCCP.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/tag/PCCP.json diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/Virtual DOM.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/Virtual DOM.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/Virtual DOM.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/tag/Virtual DOM.json diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/all.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/all.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/all.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/tag/all.json diff --git a/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/bfs.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/bfs.json new file mode 100644 index 0000000..385e87e --- /dev/null +++ b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/bfs.json @@ -0,0 +1 @@ +{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs"],"currentTag":"bfs","posts":[{"slug":"posts/Algorithm/[프로그래머스lv3]아이템 줍기","title":"[프로그래머스 level 3] 아이템 줍기","date":"2024-07-12T16:34:24.000Z","image":"index.png","summary":"bfs를 활용한 프로그래머스 [level 3] 아이템 줍기 문제 풀이 입니다.","tags":["Algorithm","프로그래머스","lv3","bfs"],"content":"# [level 3] 아이템 줍기 (BFS/DFS)\r\n## 문제 설명\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EB%AC%B8%EC%A0%9C-%EC%84%A4%EB%AA%85)\r\n\r\n다음과 같은 다각형 모양 지형에서 캐릭터가 아이템을 줍기 위해 이동하려 합니다.\r\n\r\n![1.png](1.png)\r\n\r\n지형은 각 변이 x축, y축과 평행한 직사각형이 겹쳐진 형태로 표현하며, 캐릭터는 이 다각형의 둘레(굵은 선)를 따라서 이동합니다.\r\n\r\n만약 직사각형을 겹친 후 다음과 같이 중앙에 빈 공간이 생기는 경우, 다각형의 가장 바깥쪽 테두리가 캐릭터의 이동 경로가 됩니다.\r\n\r\n![2.png](2.png)\r\n\r\n단, 서로 다른 두 직사각형의 x축 좌표 또는 y축 좌표가 같은 경우는 없습니다.\r\n\r\n![rect_3.png](3.png)\r\n\r\n즉, 위 그림처럼 서로 다른 두 직사각형이 꼭짓점에서 만나거나, 변이 겹치는 경우 등은 없습니다.\r\n\r\n다음 그림과 같이 지형이 2개 이상으로 분리된 경우도 없습니다.\r\n\r\n![rect_4.png](4.png)\r\n\r\n한 직사각형이 다른 직사각형 안에 완전히 포함되는 경우 또한 없습니다.\r\n\r\n![rect_5.png](5.png)\r\n\r\n지형을 나타내는 직사각형이 담긴 2차원 배열 rectangle, 초기 캐릭터의 위치 characterX, characterY, 아이템의 위치 itemX, itemY가 solution 함수의 매개변수로 주어질 때, 캐릭터가 아이템을 줍기 위해 이동해야 하는 가장 짧은 거리를 return 하도록 solution 함수를 완성해주세요.\r\n\r\n##### 제한사항\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EC%A0%9C%ED%95%9C%EC%82%AC%ED%95%AD)\r\n\r\n- rectangle의 세로(행) 길이는 1 이상 4 이하입니다.\r\n- rectangle의 원소는 각 직사각형의 [좌측 하단 x, 좌측 하단 y, 우측 상단 x, 우측 상단 y] 좌표 형태입니다.\r\n - 직사각형을 나타내는 모든 좌표값은 1 이상 50 이하인 자연수입니다.\r\n - 서로 다른 두 직사각형의 x축 좌표, 혹은 y축 좌표가 같은 경우는 없습니다.\r\n - 문제에 주어진 조건에 맞는 직사각형만 입력으로 주어집니다.\r\n- charcterX, charcterY는 1 이상 50 이하인 자연수입니다.\r\n - 지형을 나타내는 다각형 테두리 위의 한 점이 주어집니다.\r\n- itemX, itemY는 1 이상 50 이하인 자연수입니다.\r\n - 지형을 나타내는 다각형 테두리 위의 한 점이 주어집니다.\r\n- 캐릭터와 아이템의 처음 위치가 같은 경우는 없습니다.\r\n\r\n----------\r\n\r\n- 전체 배점의 50%는 직사각형이 1개인 경우입니다. \r\n \r\n- 전체 배점의 25%는 직사각형이 2개인 경우입니다. \r\n \r\n- 전체 배점의 25%는 직사각형이 3개 또는 4개인 경우입니다. \r\n \r\n\r\n----------\r\n\r\n##### 입출력 예\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88)\r\n\r\n\r\n\r\n \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n
rectanglecharacterXcharacterYitemXitemYresult
[[1,1,7,4],[3,2,5,5],[4,3,6,9],[2,6,8,8]]137817
[[1,1,8,4],[2,2,4,9],[3,6,9,8],[6,3,7,7]]976111
[[1,1,5,7]]11479
[[2,1,7,5],[6,4,10,10]]3171015
[[2,2,5,5],[1,3,6,4],[3,1,4,6]]146310
\r\n\r\n##### 입출력 예 설명\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/87694.%E2%80%85%EC%95%84%EC%9D%B4%ED%85%9C%E2%80%85%EC%A4%8D%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88-%EC%84%A4%EB%AA%85)\r\n\r\n입출력 예 #1\r\n\r\n![rect_5.png](6.png)\r\n\r\n캐릭터 위치는 (1, 3)이며, 아이템 위치는 (7, 8)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n입출력 예 #2\r\n\r\n![rect_7.png](7.png)\r\n\r\n캐릭터 위치는 (9, 7)이며, 아이템 위치는 (6, 1)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n입출력 예 #3\r\n\r\n[![rect_8.png](8.png)]\r\n\r\n캐릭터 위치는 (1, 1)이며, 아이템 위치는 (4, 7)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.\r\n\r\n## 구현 방법\r\n### 전체 직사각형들을 합친 것의 바깥쪽 테두리만 돌게하려면 어떻게 하는가?\r\n- rectangle을 순회하면서 범위 중 직사각형 내부는 0으로 테두리는 1로 board값을 갱신한다.\r\n- 이 때, 만일 `다른 직사각형의 내부안에 현재 직사각형의 테두리가 있지 않을 경우`에만 board를 1로 갱신한다. \r\n### 예외 사항\r\n![exception.png](exception.png)\r\n좌표가 인접한 경우 위와 같이 의도하지 않은 경우를 초래할 수도 있다.\r\n\r\n따라서 이를 해결하기 위해 모든 좌표의 값을 **2배**씩 해주어야 한다.\r\n\r\n이를 모두 반영하여 board 테이블을 만들면,\r\n\r\n```python\r\nboard = [[-1 for _ in range(102)] for _ in range(102)];\r\nfor r in rectangle:\r\n\t# map 객체를 통해 모든 좌표값에 2배\r\n\tx1,y1,x2,y2 = map(lambda x: x*2,r);\r\n for i in range(x1,x2+1):\r\n for j in range(y1,y2+1):\r\n\t # 직사각형 내부의 경우 0으로 개신\r\n if x1 {\r\n const [,triggerRender] = useState()\r\n let state = 'hello'\r\n\r\n function handleButtonClick() {\r\n state = 'hi'\r\n triggerRender()\r\n }\r\n return (\r\n <>\r\n

{state}

\r\n \r\n \r\n )\r\n}\r\n```\r\nuseState 반환값의 두 번째 원소를 실행해 리액트에 렌더링이 일어나게끔 변경했다. \r\n그럼에도 여전히 버튼 클릭시 state의 변경된 값이 렌더링되고 있지 않다. \r\n\r\n그 이유는 리액트의 렌더링은 **함수 컴포넌트에서 반환한 결과물인 return의 값을 비교해 실행되기 때문이다.** \r\n\r\n즉, 매번 렌더링이 발생할 때마다 함수는 새롭게 실행이 되고, 실행한 함수에서 state는 매번 hello로 초기화 되므로 아무리 state를 변경해도 hello로 초기화 되는 것이다. \r\n\r\n근데 렌더링이 될 때마다 초기화되는 변수(값)와는 달리, useState의 결과값은 어떻게 그 값을 유지할까?\r\n\r\n\r\n\r\n그럼 우리가 알고있는 useState는 대체 어떻게 구현이 되있는 것일지 한번 최대한 비슷하게 구현한 코드를 살펴보자.\r\n\r\n**먼저 useState의 결과 값이 유지되도록, state를 함수로 하여 state 값을 호출할 때마다 현재 state를 반환하게 해보자.**\r\n```js\r\nfunction useState(initialValue) {\r\n\tlet initialState = initialValue;\r\n\t\r\n\tfunction state() {\r\n\t\treturn initialState\r\n\t}\r\n\tfunction setState(newValue) {\r\n\t\tinitialState = newValue\r\n\t}\r\n\treturn [state, setState];\r\n}\r\nconst [value, setState] = useState(0);\r\nsetValue(1);\r\nconsole.log(value()); // 1\r\n```\r\n위의 코드도 나쁘진 않지만, 우리에게 익숙한 useState훅은 state를 함수가 아닌 상수처럼 사용하고 있다.\r\n어떻게 그게 가능한 걸까?\r\n\r\n## 클로저를 이용해 상태를 관리하는 useState\r\n이를 위해서 리액트는 **클로저**를 이용한 것이다. \r\nuseState는 클로저를 통해 useState 내부의 선언된 함수(setState)가 함수의 실행이 종료된 이후(useState가 호출된 이후)에도 지역변수인 state를 계속 참조할 수 있다.\r\n\r\nuseState 작동 방식을 대략적으로 흉내 낸 코드는 다음과 같다.\r\n\r\n```js\r\nconst MyReact = (function() {\r\n\tconst global = {}\r\n let index = 0\r\n \r\n function useState(initialState){\r\n \tif(!global.states) {\r\n \t// 애플리케이션 전체의 states 배열 초기화, 최초 접근이면 빈 배열로\r\n \tglobal.states = []\r\n }\r\n // states 정보를 조회해서, 현재 상태값이 있는지 확인\r\n // 없다면 초깃값으로 설정\r\n const currentState = global.states[index] || initialState\r\n // 위에서 조회한 값으로 states의 값 업데이트\r\n global.states[index] = currentState\r\n \r\n // 즉시실행함수로 setter 만듬\r\n const setState = (function() {\r\n \t// 클로저로 index를 가둬두어서 동일한 index에 접근이 가능\r\n \tlet currentIndex = index\r\n return function(value){\r\n \tglobal.states[currentIndex] = value\r\n //컴포넌트 렌더링이 들어가는 부분이다.(실제 코드는 생략)\r\n }\r\n })()\r\n // useState를 쓸 때마다 index를 하나씩 추가하는데, 이는 하나의 state마다\r\n // index가 할당되어있어, 그 index가 배열의 값(global.states)를 가리키고,\r\n // 필요할 때마다 그 값을 가져오게 하는 것이다.\r\n index = index + 1\r\n \r\n return [currentState,setState]\r\n}\r\n\r\nfunction Component() {\r\n\tconst [value, setValue] = useState(0);\r\n}\r\n})();\r\n```\r\n\r\n실제 리액트 코드에서는 useReducer를 이용해 구현되어 있어 약간의 차이가 있다.\r\n\r\n아무튼 여기서 함수의 실행이 끝났음에도 함수가 선언된 환경을 기억할 수 있는 방법이 바로 클로저인 것이다. 만약 클로저가 없다면, `setState`는 항상 `index`의 현재 값에 의존하게 된다. 즉, 컴포넌트가 여러 상태를 갖고 있을 때 마지막 `index`만 참조하므로, `setState`가 올바른 위치를 참조하지 않게 되는 것이다.\r\n\r\n매번 실행되는 함수 컴포넌트 환경에서 state의 값을 유지하고 사용하기 위해 리액트는 클로저를 활용하고 있다.\r\n\r\n`\r\n훅에 대한 구현체를 github에서 타고 올라가다보면 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 라는 문구를 만나게된다(무섭다 ㅋㅋ)\r\n위의 코드는 Preact의 구현을 기준으로 하고 있다. Preact는 react의 경량화 버전으로,\r\n대부분의 리액트 API를 지원하고 있다. \r\n`\r\n## 결론\r\nReact의 `useState`는 클로저를 통해 상태값을 안정적으로 유지하며, 함수 컴포넌트가 여러 번 호출되더라도 **각 상태값이 고유한 위치에 저장**될 수 있게 한다. `useState`가 반환하는 `setState` 함수는 생성 당시의 상태 위치(`index`)를 클로저로 캡처하여, 해당 상태값만 정확히 업데이트하도록 구현되어 있다.\r\n\r\n정리하자면, 클로저는 `setState`가 함수가 선언된 당시의 환경을 유지하게 해주기 때문에 **컴포넌트가 매번 재실행될 때마다 상태가 초기화되는 것을 방지**하고, 상태가 올바르게 유지될 수 있게 해준다.\r\n\r\n**참고**\r\n\r\n[서적] 모던 리액트 Deep Dive\r\n"},{"slug":"posts/Javascript/클로저에 대하여","title":"클로저에 대하여","date":"2024-10-25T20:00:24.000Z","image":"index.png","summary":"자바스크립트의 어렵지만 중요한 클로저의 개념과 활용 이해해보자","tags":["JavaScript","deepdive","클로저","생명주기"],"content":"# 클로저에 대하여\r\n\r\n면접 질문 중 단골이고, 자바스크립트에 관심이 있다면 한번쯤 들어봤을 개념인 **클로저**.\r\n\r\n사실 많이 난해한 개념이기도 하고, 필자 역시 전에 한번 공부를 해보았지만 아직 확실히 와닿지는 않는 개념이다. \r\n\r\n그래서 이번 기회에 제대로 정리하고 넘어가고자 한다. 함께 이 개념이 대체 뭔지 살펴보자.\r\n\r\n## 클로저의 정의\r\n> 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.\r\n\r\n'이게 뭔소리지?' 싶은가? 나도 그러하다. 위의 정의에서 이해하여야 할 핵심 키워드는 **함수가 선언된 렉시컬 환경**이다.\r\n\r\n ```js\r\n const x = 1;\r\n function outerFunc( ) {\r\n\t const x = 10;\r\n\t function innerFunc( ) {\r\n\t\t console.log(x); // 10\r\n\t}\r\n\tinnerFunc( );\r\n}\r\nouterFunc( );\r\n```\r\n`outerFunc` 내부에서 중첩 함수 `innerFunc`가 정의되고 호출되었다. \r\n\r\n따라서 중첩 함수 `innerFunc` 내부에서 자신을 포함하고 있는 `outerFunc`의 x 변수에 접근할 수 있다. **만약 innerFunc 함수가 outerFunc의 내부에서 정의되지 않았다면, 즉 외부에서 별도로 정의 되었다면** innerFunc를 outerFunc 내부에서 호출해도 outerFunc 함수의 변수에 접근이 불가능하다.\r\n\r\n ```js\r\n const x = 1;\r\n function outerFunc( ) {\r\n\t const x = 10;\r\n\t// 안에서 호출되었지만 접근 불가능\r\n\t innerFunc( );\r\n}\r\nfunction innerFunc( ) {\r\n\t// 상위 스코프인 전역에서 선언된 1\r\n\tconsole.log(x); // 1\r\n}\r\nouterFunc( );\r\n```\r\n\r\n위와 같은 현상은 자바스크립트가 **렉시컬 스코프**를 따르기에 발생한다.\r\n\r\n## 렉시컬 스코프\r\n자바스크립트 엔진은 함수를 **어디서 호출했느냐**가 아니라 함수를 **어디서 정의했는지**에 따라 **상위 스코프를 결정** 한다. \r\n\r\n이를 **렉시컬 스코프**라고 한다.\r\n\r\n위의 예제코드를 다시한번 본다면, outerFunc와 innerFunc는 모두 전역에서 정의 되었고, 함수의 상위 스코프는 함수를 어디서 정의했는지에 따라 결정되므로 **두 함수의 상위 스코프는 모두 전역이다.**\r\n\r\n함수의 상위 스코프는 결국, **함수의 정의된 위치에 따라 정적으로 결정**되고, 함수의 호출된 위치는 어떠한 영향도 주지 못한다.\r\n\r\n>렉시컬환경: **변수를 저장하고 외부 스코프와 연결을 유지하는 객체**라고 생각하면된다. 자바스크립트에서는 함수가 생성될 때마다 렉시컬 환경이 만들어지며, 함수 내부의 변수뿐 아니라 함수가 선언된 위치에 있는 외부 변수도 기억하게된다.\r\n>\r\n> 코드가 위치한 곳에 따라 **변수와 함수를 어디서 찾아볼지 알려주는 일종의 \"지도\"**라고 생각할 수도 있다\r\n\r\n\r\n렉시컬 환경은 자신의 **외부 렉시컬 환경에 대한 참조**를 통해 상위 렉시컬 환경과 연결이된다.\r\n\r\n따라서 함수의 상위 스코프를 결정한다는 것은, 현재 함수의 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값을 결정한다는 것을 의미한다.\r\n\r\n렉시컬 스코프를 다시한번 정의해보자면, 렉시컬 환경의 \"외부 렉시컬 환경에 대한 참조\"에 저장한 참조값, 즉 상위 스코프에 대한 참조는 **함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)**에 의해 결정되는 것이라고 할 수 있다. \r\n\r\n\r\n\r\n**함수는 자신의 내부슬롯 [[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.** \r\n이곳을 참조해서 자신이 호출되었을 때 생성될 함수 렉시컬환경의 \"외부 렉시컬 환경에 대한 참조\"에 저장될 참조값을 보고, 자신이 존재하는 한, 이 [[Environment]] 슬롯에 저장한 렉시컬 환경의 참조, 즉 **상위 스코프**를 기억한다.\r\n\r\n## 클로저와 렉시컬환경\r\n그렇다면 다음의 코드를 살펴보자\r\n```js\r\nconst x = 1 ;\r\nfunction outer() {\r\n\tconst x = 10;\r\n\tconst inner = function ( ) {console.log(x)}\r\n\t//inner함수 반환\r\n\treturn inner;\r\n}\r\n// outer함수를 호출하면 중첩 함수 inner를 반환한다.\r\n// 그리고 outer 함수의 실행 컨텍스트는 제거된다.\r\nconst innerFunc = outer( );\r\ninnerFunc( )// 10\r\n```\r\nouter함수를 호출하면 outer함수는 중첩함수 inner를 반환하고 생명주기를 마감한다. \r\n\r\n즉 outer함수의 실행이 종료되었으므로, 실행컨텍스트가 제거된다. (실행컨텍스트 스택에서 pop된다.)\r\n\r\n이때 outer 함수의 지역변수x 역시 생명주기를 마감했으므로, 실행 컨텍스트가 제거되어 유효하지 않아 보인다.\r\n\r\n그러나 위의 실행 결과는 outer 지역 변수x 의 값인 10을 반환한다. 이미 생명 주기가 종료되어 outer 함수의 지역변수 x가 실행 컨텍스트 스택에서 제거되었는데도 다시 부활이라도 한 것 마냥 말이다. \r\n\r\n이처럼 **외부 함수보다 중첩함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명주기가 종료한 외부함수의 변수를 참조**할 수 있다. \r\n\r\n이러한 **중첩 함수를 클로저**라고 부른다.\r\n\r\n다시 정의로 돌아가보자.\r\n> 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.\r\n\r\n위 정의를 예제에 대입해보자면 `함수`는 반환된 `중첩함수(inner)`를 의미하고 `그 함수가 선언될 때의 렉시컬 환경(Lexical environment)`란 그 `중첩 함수(inner)가 정의됐을 때의 스코프`를 의미하는 것이다.\r\n\r\n즉, **클로저는 반환된 중첩 함수가 자신이 선언됐을 때의 렉시컬 환경 즉, 스코프를 기억하여 자신이 선언됐을 때의 렉시컬 환경 밖에서 호출되어도 그 렉시컬 환경(스코프)에 접근할 수 있는 함수**를 말한다. \r\n\r\n조금 더 간단히 말하면 **클로저는 자신이 생성될 때의 상위 스코프(렉시컬 환경)을 기억하는 함수이다**\r\n\r\n\r\n\r\n\r\ninner 함수의 [[Environment]] 슬롯이 outer 함수의 렉시컬 환경을 참조하고, inner가 전역 변수 innerFunc에 저장되어 계속 사용되고 있으므로, 이와 연결된 모든 요소는 가비지 컬렉터에 의해 제거되지 않습니다.\r\n\r\n\r\n> **가비지 컬렉터(Garbage Collector)는 프로그램이 더 이상 사용하지 않는 메모리를 자동으로 해제해 주는 자바스크립트 엔진의 기능**이다.\r\n> \r\n> 가비지 컬렉터는 특정 메모리 공간이 더 이상 참조되지 않을 때 그 공간을 \"가비지\"로 판단하여 메모리를 해제한다. 반대로, **누군가가 참조하고 있는 메모리 공간은 함부로 해제하지 않는다**.\r\n\r\n클로저는 자바스크립트의 강력한 기능으로, 필요하다면 적극 활용해야 한다. 클로저가 유용하게 사용되는 상황을 살펴보자.\r\n\r\n## 클로저의 활용\r\n\r\n### 상태를 안전하게 변경하고 유지할 때\r\n\r\n클로저는 **상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고, 특정 함수에게만 상태 변경을 허용하여, 상태를 안전하게 변경하고 유지할 수 있도록 사용한다.**\r\n```js\r\nconst counter = (function ( ) {\r\n// 은닉된 상태 (외부에서 접근 불가)\r\nlet num = 0;\r\n// 클로저인 메서드를 갖는 객체를 반환한다.\r\n// 객체 리터럴은 스코프를 만들지 않는다.\r\n// 따라서 아래 메서드들의 상위 스코프는 즉시 실행 함수의 렉시컬 환경이다.\r\nreturn {\r\n\tincrease() {\r\n\t\treturn ++num;\r\n\t}\r\n\tdecrease() {\r\n\t\treturn num>0? --num: 0;\r\n\t}\r\n}());\r\n\r\nconsole.log(counter.increase()); // 1\r\nconsole.log(counter.decrease()); // 2\r\n\r\nconsole.log(counter.decrease()); // 1\r\nconsole.log(counter.decrease()); // 0\r\n```\r\n즉시 실행 함수는 호출된 이후 소멸되지만, 즉시 실행 함수가 반환한 클로저(increase, decrease)는 **자신의 상위 스코프인 즉시 실행함수의 렉시컬 환경을 기억하고 있다**. \r\n\r\n이 코드에서 \r\n- 카운터 상태(num 변수의 값)은 increase, decrease 함수가 호출되기 전까지 변경되지않고 유지되며 **외부에서 접근할 수 없다**.\r\n- 카운터 상태는 오직 **increase, decrease로 정의된 함수로만 변경이 가능**하다.\r\n\r\n\r\n\r\n이처럼 클로저는 **상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고 특정함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용**된다.\r\n\r\n### 전역변수의 사용을 억제할 때\r\n\r\n클로저는 전역 변수 사용을 억제하고 대신 **함수 내부의 변수로 상태를 관리**할 수 있도록 도와준다. 이렇게 하면 전역 변수를 사용하지 않고도 데이터가 안전하게 유지되며, 다른 코드와 충돌하지 않는 이점을 얻을 수 있다.\r\n\r\n```js\r\nlet counter = 0; // 전역 변수\r\n\r\nfunction incrementCounter() {\r\n counter += 1;\r\n return counter;\r\n}\r\n\r\nconsole.log(incrementCounter()); // 1\r\nconsole.log(incrementCounter()); // 2\r\nconsole.log(counter); // 전역 변수에 접근 가능 (위험)\r\n```\r\n위 코드에서는 `counter`가 전역에 선언되어 있기 때문에 어디서든 접근 가능하여, 이러면 다른 코드에서 `counter`를 실수로 변경할 위험이 있다.\r\n```js\r\nfunction createCounter() {\r\n let counter = 0; // 함수 내부 변수로 관리\r\n\r\n return function() {\r\n counter += 1;\r\n return counter;\r\n };\r\n}\r\n\r\nconst incrementCounter = createCounter();\r\n\r\nconsole.log(incrementCounter()); // 1\r\nconsole.log(incrementCounter()); // 2\r\nconsole.log(typeof counter); // 'undefined' - 전역에서 접근 불가\r\n```\r\n\r\n이 코드에서는 `counter` 변수가 **`createCounter` 함수 내부에만 존재**하므로 외부에서 직접 접근하거나 수정할 수 없다. \r\n\r\n대신, `incrementCounter` 함수는 **클로저를 통해 `counter`를 기억**하고 있으므로 호출할 때마다 `counter`를 안전하게 증가시킬 수 있다.\r\n\r\n이처럼, 클로저를 사용하면 함수 내 지역 변수를 통해 상태를 관리하게 되어 **전역 변수를 사용하지 않아도 안전하게 데이터 상태를 유지**할 수 있다.\r\n\r\n### React의 useState\r\n\r\n이런 이점을 활용하여 react의 useState에도 클로저가 활용된다.\r\n\r\n`useState`를 사용하면 컴포넌트 내에서 **상태를 관리**하게 되는데, 이 상태가 **컴포넌트가 렌더링될 때마다 유지**되도록, 또한 `setState` 함수로만 상태가 변경되도록 하는 데에 클로저가 활용되는 것이다.\r\n\r\n이와 관련하여서는 다음 포스팅에서 자세하게 다룰 예정이다.\r\n\r\n\r\n## 결론\r\n\r\n지금까지 자바스크립트의 어렵지만 주요한 개념인 클로저에 대해 살펴보았다.\r\n\r\n**클로저**는 **함수와 그 함수가 선언된 렉시컬 환경의 조합으로, 자바스크립트에서 함수는 자신이 정의된 위치에 따라 상위 스코프를 결정하는 렉시컬 스코프**를 따르므로, \r\n중첩 함수가 외부 함수의 스코프를 참조하여 외부함수가 생명주기가 끝났음에도 그 함수의 변수를 참조할 수 있는 함수를 의미한다."},{"slug":"posts/React/[React DeepDive] React의 가상 DOM","title":"[React DeepDive] React의 가상 DOM","date":"2024-09-25T12:00:24.000Z","image":"React.svg","summary":"리액트 딥다이브 책 내용 중 '가상 DOM과 파이버'의 정리 내용입니다.","tags":["react","deepdive","Virtual DOM"],"content":"# [React DeepDive] React의 가상 DOM\r\n\r\n\r\n리액트의 특징 중 하나는 실제 DOM이 아닌 가상 DOM을 운영한다는 것이다. 이번 글에서는 React DeepDive에서 다루는 **가상 DOM이 무엇인지**, **그리고 실제 DOM에 대해 어떤 이점이 있는지 살펴보고**, 가상 DOM을 다룰 때 **주의할 점**에 대해서도 다루려고 한다.\r\n\r\n## DOM과 브라우저 렌더링 과정\r\n> **DOM**: 웹페이지에 대한 인터페이스로 브라우저가 웹페이지의 콘텐츠와 구조를 어떻게 보여줄지에 대한 정보를 담고 있다.\r\n\r\n브라우저가 웹사이트 접근 요청을 받고 화면을 그리는 과정은 다음과 같다.\r\n\r\n\"트리를\r\n\r\n\r\n1. 브라우저가 사용자 요청한 주소에서 HTML파일을 다운로드한다.\r\n\r\n2. 브라우저의 렌더링 엔진이 HTML을 파싱해 DOM노드로 구성된 트리 즉, `DOM`을 만든다.\r\n3. 2번 과정에서 CSS파일을 만나면 해당 CSS 파일도 다운로드한다.\r\n4. 브라우저의 렌더링 엔진이 이 CSS역시 파싱해 CSS 노드로 구성된 트리 즉, `CSSOM`을 만든다\r\n5. 브라우저는 DOM 노드를 순회하는데 모든 노드가 아닌, **사용자 눈에 보이는 노드만**(display:none과 같은 노드는 방문 x) 방문한다.\r\n6. 눈에 보이는 노드를 대상으로 해당 노드에 대한 CSSOM 정보를 찾고 여기서 발견한 CSS 스타일 정보를 이 노드에 적용한다. \r\n>\t- `레이아웃`: 각 노드가 브라우저 화면의 어느 좌표에 정확히 나타나야 하는지 계산하는 과정이며, 이 과정을 거치면 **페인팅 과정도 거치게 된다.**\r\n>- `페인팅`: 레이아웃 단계를 거친 노드의 색과 같은 **실제 유효한 모습을 그리는 과정**\r\n\r\n## 가상 DOM의 탄생 배경\r\n\r\n### 웹페이지를 추가로 렌더링 하는데 드는 비용\r\n앞에서 살펴본 브라우저가 웹페이지를 렌더링하는 과정은 매우 복잡하고 많은 비용이 든다. \r\n\r\n또한 정보를 보여주는데 그치지 않고 사용자의 인터렉션을 통해 다양한 정보를 노출해야 하기에, 렌더링이 완료된 이후에도 **사용자의 인터렉션으로 웹페이지가 변경되는 상황** 또한 고려해야 한다.\r\n\r\n이 과정에서 예를 들어,\r\n- 특정 요소의 색상이 변경되는 경우: 페인팅만이 일어나서 빠른처리가 가능하다.\r\n\r\n- **특정 요소의 노출 여부나 사이즈가 변경되는 경우**: 레이아웃이 일어나고, 레이아웃은 **필연적으로 리페인팅을 발생**하기 때문에 더 많은 비용이 든다.\r\n\r\n- DOM 변경이 일어나는 요소가 **자식 요소를 많이 가지고 있는 경우**: 하위 자식 요소 역시 변경되야 해서 더 많은 비용 지불\r\n\r\n이러한 추가 렌더링 작업은 하나의 페이지에서 모든 작업이 일어나는 `싱글 페이지 애플리케이션(SPA)`에서 더 많아진다. \r\n\r\n페이지가 변경될 때 처음부터 HTML을 새로 받아서 다시금 렌더링 과정을 시작하는 일반적인 웹페이지와는 다르게, 하나의 페이지에서 계속해서 요소의 위치를 재계산하게 된다.\r\n\r\n그러므로 라우팅이 변경되는 경우 고정된 헤더와 같은 요소들을 제외하고 대부분의 요소를 삭제, 삽입 및 요소의 위치를 다시 계산해야 하므로, DOM을 관리하는 과정에서 부담하는 비용이 커진다.\r\n\r\n### 가상 DOM의 탄생\r\n사용자의 인터렉션에 따라 DOM의 모든 변경 사항을 추적하는 것은 개발자에게는 너무나 수고스러운 일이다. 그렇기에 모든 DOM의 변경보다 **결과적으로 만들어지는 DOM 결과물 하나만 아는 것**이 개발자의 입장에서 더 유용할 것이다.\r\n\r\n이것을 해결하기 위해 탄생한 것이 바로 `가상 DOM`이다. \r\n가상 DOM은 실제 브라우저가 아닌 리액트가 관리하는 가상의 DOM을 의미한다.\r\n\r\n가상 DOM은 웹페이지가 표시해야 할 DOM을 일단 **메모리에 저장**하고, 리액트가 실제 변경에 대한 준비가 완료되었을 때, 실제 브라우저의 DOM에 반영한다.\r\n\r\n이렇게 DOM 계산을 메모리에서 계산하는 과정을 한 번 거치게 된다면 실제로는 여러 번 발생했을 렌더링 과정을 최소화하여 부담을 덜 수 있다.\r\n\r\n> 가상 DOM은 **일반적인 브라우저보다 무조건 항상 빠르지는 않다**. \r\n>\r\n> 무조건 빠른 것이 아닌, 대부분의 상황에서 웬만한 애플리케이션을 만들 정도로 충분히 빠르다고 보는 것이 옳다.\r\n\r\n## 가상 DOM을 위한 아키텍처, 리액트 파이버\r\n가상 DOM과 렌더링 과정 최적화를 가능하게 해주는 것이 `리액트 파이버`이다.\r\n\r\n`리액트 파이버`는 리액트에서 관리하는 평범한 자바스크립트 객체이다. \r\n`파이버`는 `파이버 재조정자(fiber reconciler)`가 관리하는데, 가상 DOM과 실제 DOM을 비교하여 변경에 관련된 정보를 가지고 있는 파이버를 기준으로 화면에 렌더링을 요청하는 역할을 한다.\r\n\r\n> 재조정(reconcilation): 리액트에서 어떤 부분을 새로 렌더링 해야하는지 가상 DOM과 실제 DOM을 비교하는 과정 \r\n\r\n파이버는 애니메이션,레이아웃,사용자 인터랙션에 올바른 결과물을 만드는 반응성 문제를 해결하며, 다음과 같은 일을 할 수 있다.\r\n- 작업을 작은 단위로 쪼개고 **우선순위를 매긴다.**\r\n\r\n- 이 작업들은 일시 정지 및 다시 시작이 가능하다.\r\n\r\n- 이전 작업을 재사용 하거나 필요 없는 경우에는 폐기할 수 있다.\r\n\r\n이러한 모든 과정은 **비동기로 일어난다**. 과거에는 이러한 조정 알고리즘이 동기적인 스택 알고리즘으로 이뤄져 있었고, 동기적으로 작업이 이뤄졌기에 자바스크립트의 싱글 스레드의 특징 상 수행 중인 작업은 중단될 수 없었다. \r\n이러한 문제 때문에 리액트 팀은 스택 조정자 대신 파이버라는 개념을 탄생시킨다.\r\n\r\n### 파이버는 어떻게 구현되어 있을까?\r\n`파이버`는 일단 하나의 작업 단위로 구성되어 있다. 리액트는 작업 단위를 하나씩 처리하고, **finishWork( )** 라는 작업으로 마무리한다. 그리고 이 작업을 커밋해 브라우저 DOM에 가시적인 변경 사항을 만들어 낸다. \r\n이러한 단계는 두 단계로 나뉘는데,\r\n- `렌더 단계`: 사용자에게 노출되지 않는 모든 **비동기** 작업을 수행하고, **우선 순위를 지정하거나 중지시키거나 버리는 등의 작업이 일어난다.**\r\n- `커밋 단계`: DOM에 실제 변경 사항을 반영하기 위한 작업, commitWork( )가 실행되는데, **동기식**으로 일어나고 중단될 수 없다.\r\n\r\n파이버는 실제 리액트 코드에서 이렇게 구현되어 있다.\r\n```js\r\nfunction FiberNode(tag, pendingProps, key, mode) {\r\n // Instance\r\n this.tag = tag;\r\n this.key = key;\r\n this.elementType = null;\r\n this.type = null;\r\n this.stateNode = null;\r\n\r\n // Fiber\r\n this.return = null;\r\n this.child = null;\r\n this.sibling = null;\r\n this.index = 0;\r\n\r\n this.ref = null;\r\n\r\n this.pendingProps = pendingProps;\r\n this.memoizedProps = null;\r\n this.updateQueue = null;\r\n this.memoizedState = null;\r\n this.dependencies = null;\r\n\r\n this.mode = mode;\r\n\r\n // Effects\r\n this.effectTag = NoEffect;\r\n this.nextEffect = null;\r\n\r\n this.firstEffect = null;\r\n this.lastEffect = null;\r\n\r\n this.expirationTime = NoWork;\r\n this.childExpirationTime = NoWork;\r\n\r\n this.alternate = null;\r\n}\r\n```\r\n위와 같이 파이버는 단순한 자바스크립트 객체로 구성되어 있다. \r\n파이버와 리액트 요소의 한가지 중요한 차이점은 `리액트 요소`는 렌더링이 발생할 때 마다 새롭게 생성되지만, `파이버`는 컴포넌트가 최초로 마운트 되는 시점에 생성되어 **가급적이면 재사용된다**는 것이다.\r\n\r\n```js\r\nfunction createFiber(tag, pendingProps, key, mode) {\r\n return new FiberNode(tag, pendingProps, key, mode);\r\n}\r\n```\r\n```js\r\nfunction createFiberFromElement(element, mode, expirationTime) {\r\n let owner = null;\r\n const type = element.type;\r\n const key = element.key;\r\n const pendingProps = element.props;\r\n const fiber = createFiberFromTypeAndProps(\r\n type,\r\n key,\r\n pendingProps,\r\n owner,\r\n mode,\r\n expirationTime,\r\n );\r\n\r\n return fiber;\r\n}\r\n```\r\n이제 여기서 선언된 주요 속성을 살펴보면서 어떤 내용을 담고 있는지 살펴보자.\r\n\r\n- `tag` : 파이버는 하나의 element에 하나의 파이버가 생성되어 1:1 관계를 가진다. 여기서 **1:1로 매칭된 정보를 가지고 있는 것**이 tag필드이다. 연결되는 것은 컴포넌트, DOM노드 등이 될 수 있는데, 될 수 있는 것들은 다음과 같다.\r\n- `stateNode`: 이 속성은 파이버 자체에 대한 참조 정보를 가지고 있다.\r\n- `child`, `sibling`, `return` : 파이버 간의 관계 개념을 나타내는 속성이다. 파이버는 트리 형식을 구성하는데 이 트리 형식을 구성하는데 필요한 정보가 이 속성 내부에 적용된다. 한 가지 리액트 컴포넌트 트리와 다른 점은 children이 없다는 것 즉 **단 하나**의 `child`만이 존재한다는 것이다.\r\n\r\n\t여러 개의 자식이 존재할 경우 항상 **첫 번째 자식의 참조**로 구성되며, 나머지는 자식들은 동등한 `sibling`으로 구성, `return`은 부모 파이버를 의미한다.\r\n\r\n- `index` : 여러 형제들 사이에서 자신의 위치를 나타낸다.\r\n- `pedingProps` : 아직 처리하지 못한 props\r\n- `memoizedProps` : pendingProps를 기준으로 렌더링이 완료된 이후에 pendingProps를 memoizedProps에 저장하여 관리한다.\r\n- `updateQueue` : 상태 업데이트, 콜백 함수, DOM 업데이트 등 작업을 담아두는 Queue.\r\n- `memoizedState` : 함수 컴포넌트의 훅 목록이 저장된다.\r\n- `alternate`: 뒤이어 설명할 리액트 파이버 트리와 이어질 개념. 리액트의 트리는 두 개인데, alternate는 반대 트리의 파이버를 가리킨다.\r\n\r\n이렇게 생성된 파이버는 state가 변경되거나 생명주기 메서드가 실행되거나 DOM의 변경이 필요한 시점 등에 실행된다. \r\n이러한 작업들은 작은 단위로 나눠서 처리할 수도, 애니메이션과 같이 우선순위가 높은 작업은 빨리 처리하거나, 낮은 작업을 연기시키는 등 좀 더 유연하게 처리된다.\r\n\r\n\r\n리액트 개발 팀은 사실 리액트는 가상 DOM이 아닌 Value UI, 즉 값을 가지고 있는 UI를 관리하는 라이브러리라는 내용을 피력한 바가 있다. \r\n\r\n즉, 리액트의 핵심원칙은 **UI를 문자열, 숫자, 배열과 같은 값으로 관리한다는 것**이다. \r\n변수에 이러한 UI관련 값을 보관하고, 리액트의 자바스크립트 코드 흐름에 따라 이를 관리하고, 표현하는 것이 바로 리액트다.\r\n\r\n### 파이버 트리\r\n파이버 트리는 하나는 **현재 모습을 담은** 파이버 트리, 다른 하나는 작업 중인 상태를 나타내는 **workInProgress** 트리다. 작업이 끝나면, 리액트는 **단순히 포인터만 변경**해 workInProgress 트리를 **현재 트리**로 바꿔버린다. 이러한 기술을 **더블 버퍼링**이라고 한다.\r\n\"트리를\r\n\r\n리액트에서는 미처 다 그리지 못한 모습을 노출시키지 않기 위해 (불완전한 트리를 보여주지 않기 위해) 더블 버퍼링 기법을 쓰는데, 이러한 더블 버퍼링을 위해 트리가 두 개 존재하며, 이 더블 버퍼링은 커밋 단계에서 수행된다.\r\n\r\n- 먼저 현재 UI 렌더링을 위해 존재 하는 **current**를 기준으로 모든 작업이 시작된다.\r\n- 업데이트가 발생하면 파이버는 리액트에서 새로 받은 데이터로 새로운 **workInProgress** 트리를 빌드하기 시작한다.\r\n- 빌드하는 작업이 끝나면 다음 렌더링에 이 트리를 사용한다.\r\n- 빌드된 workInProgress 트리가 UI에 최종적으로 렌더링되어 반영이 완료되면 **current가 이 workProgress로 변경된다**.\r\n\r\n### 파이버의 작업 순서\r\n일반적인 파이버 노드의 생성흐름은 다음과 같다.\r\n1. 리액트는 `beginWork()` 함수를 실행해 파이버 작업을 수행하고, 더 이상 자식이 없는 파이버를 만날 때까지 트리 형식으로 시작된다.\r\n2. 1번의 작업이 끝나면 `completeWork( )` 함수를 실행해 파이버 작업을 완료한다.\r\n3. 형제가 있다면 형제로 넘어간다.\r\n4. 2,3번이 끝난다면 `return`으로 돌아가 자신의 작업이 완료되었음을 알린다.\r\n\r\n이러한 작업으로 트리가 생성이 되었는데, setState 등으로 업데이트가 발생하면 어떻게 될까? 이미 앞서 만든 current트리가 존재하고, setState로 업데이트 요청을 받아 workInProgress트리를 다시 빌드하기 시작한다. \r\n\r\n최초 렌더링 시에는 모든 파이버를 새로 만들어야 했지만 이제는 파이버가 이미 존재하므로 되도록 새로 생성하지 않고, **기존 파이버에서 업데이트 된 props를 받아 파이버 내부에서 처리한다**.\r\n\r\n이처럼, 재조정 작업 때마다 새롭게 파이버 자바스크립트 객체를 만드는 것 이 아닌, 기존의 객체를 재활용하기 위해 내부 속성값만 초기화하거나 바꾸는 형태로 트리를 업데이트한다.\r\n\r\n과거에는 이 작업을 동기식으로 처리했고, 트리 업데이트 과정 및 새로운 트리를 만드는 작업은 동기식이고 중단될 수 없다. 그러나 현재는 우선순위가 높은 다른 업데이트가 오면 현재 업데이트 작업을 일시 중단하거나 새로 만들거나 폐기할 수 있으며 작업 단위를 파이버 단위로 나누어 우선순위를 할당하는 것 역시 가능하다.\r\n\r\n### 파이버와 가상 DOM\r\n리액트 컴포넌트에 대한 정보를 **1:1로 가지고 있는 것**이 파이버이며, 이 파이버는 리액트 아키텍처 내부에서 **비동기**로 이뤄진다. 이와 달리 실제 브라우저 구조인 DOM에 반영하는 것은 동기적으로 이뤄져야 하기에, 메모리 상에서 이 작업을 먼저 수행해서 최종적인 결과물만 실제 브라우저 DOM에 적용하는 것이다.\r\n\r\n> 사실 가상 DOM은 오직 웹 어플리케이션에서만 통용되는 개념이고, 리액트 파이버는 리액트 네이티브와 같은 브라우저가 아닌 환경에서도 사용할 수 있기에 엄밀히 하면 파이버와 가상 DOM은 동일한 개념이 아니다. \r\n\r\n### 정리\r\n결국 가상 DOM과 리액트의 핵심은 **브라우저의 DOM을 더욱 빠르게 그리고 반영하는 것이 아니라 바로 값으로 UI를 표현하는 것**이다. \r\n\r\n화면에 표시되는 UI를 자바스크립트의 문자열, 배열 등과 마찬가지로 값으로 관리하고 이러한 흐름을 효율적으로 관리하기 위한 메커니즘이 바로 리액트의 핵심이다."}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/dfs.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/dfs.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/dfs.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/tag/dfs.json diff --git a/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/gold.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/gold.json new file mode 100644 index 0000000..d8f83c3 --- /dev/null +++ b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/gold.json @@ -0,0 +1 @@ +{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs","all"],"currentTag":"gold","posts":[{"slug":"posts/Algorithm/[백준 gold 5] 빌런 호석","title":"[백준 gold 5] 빌런 호석","date":"2024-08-09T17:05:24.000Z","image":"index.png","summary":"dfs을 활용한 프로그래머스 [level 3] 징검다리 건너기 문제 풀이 입니다.","tags":["Algorithm","백준","gold","dfs"],"content":"# [gold 3] 빌런 호석\r\n\r\n## 문제 설명\r\n\r\n치르보기 빌딩은 11층부터 NN층까지 이용이 가능한 엘리베이터가 있다. 엘리베이터의 층수를 보여주는 디스플레이에는 KK 자리의 수가 보인다. 수는 00으로 시작할 수도 있다. 00부터 99까지의 각 숫자가 디스플레이에 보이는 방식은 아래와 같다. 각 숫자는 7개의 표시등 중의 일부에 불이 들어오면서 표현된다.\r\n\r\n![](1.png)\r\n\r\n예를 들어 K=4K=4인 경우에 16801680층과 501501층은 아래와 같이 보인다.\r\n\r\n![](2.png)\r\n\r\n\r\n\r\n빌런 호석은 치르보기 빌딩의 엘리베이터 디스플레이의 LED 중에서 최소 11개, 최대 PP개를 반전시킬 계획을 세우고 있다. 반전이란 켜진 부분은 끄고, 꺼진 부분은 켜는 것을 의미한다. 예를 들어 숫자 11을 22로 바꾸려면 총 5개의 LED를 반전시켜야 한다. 또한 반전 이후에 디스플레이에 올바른 수가 보여지면서 11 이상 NN 이하가 되도록 바꿔서 사람들을 헷갈리게 할 예정이다. 치르보기를 사랑하는 모임의 회원인 당신은 호석 빌런의 행동을 미리 파악해서 혼쭐을 내주고자 한다. 현재 엘리베이터가 실제로는 XX층에 멈춰있을 때, 호석이가 반전시킬 LED를 고를 수 있는 경우의 수를 계산해보자.\r\n\r\n### 입력\r\n\r\n N,K,P,XN,K,P,X 가 공백으로 구분되어 첫째 줄에 주어진다.\r\n\r\n### 출력\r\n\r\n호석 빌런이 엘리베이터 LED를 올바르게 반전시킬 수 있는 경우의 수를 계산해보자.\r\n\r\n## 접근 방법\r\n\r\n숫자에 따른 LED를 먼저 구현하기 위해, \r\n\r\n그림과 같이, 각 LED의 위치에 번호를 매겨, 배열안에 그 번호가 있으면 불이 켜져있는 것으로, 없다면 불이 꺼져 있는 것으로 표현하였다.\r\n\"이미지\r\n\r\n\r\n그림처럼 0을 표현하면 3번자리를 제외한 모든 번호가 켜져있으므로, `[0,1,2,4,5,6]`으로 표현 할 수 있다.\r\n\r\n각 번호를 이와 같이 바꾸어 numbers 배열에 저장하면, 이와 같다.\r\n```python\r\nnumbers = [[0,1,2,4,5,6],[2,5],[0,2,3,4,6],[0,2,3,5,6],[1,2,3,5],[0,1,3,5,6],[0,1,3,4,5,6],[0,2,5],[0,1,2,3,4,5,6],[0,1,2,3,5,6]];\r\n```\r\n이후 만일 현재 번호와 비교할 번호의 LED를 비교하기 위해 서로의 차집합을 더해서 두 번호가 서로를 비교했을 때, 가지고 있는 혹은 없는 번호의 개수를 구한다.\r\n```python\r\ncur_count = len(list(cur.difference(compare))) + len(list(compare.difference(cur)));\r\n```\r\n이와 같은 방식으로 반전시킬 LED의 개수를 구하고, dfs를 활용하여 x의 모든 자릿수를 순회하여 p보다 작거나 같게 LED를 반전하면서, n보다 번호가 작거나 같다면 result를 1 증가 시킨다.\r\n\r\n전체코드는 다음과 같다.\r\n```python\r\nimport sys;\r\ninput = sys.stdin.readline;\r\n\r\nn,k,p,x = map(int, input().split());\r\nresult = 0;\r\nnumbers = [[0,1,2,4,5,6],[2,5],[0,2,3,4,6],[0,2,3,5,6],[1,2,3,5],[0,1,3,5,6],[0,1,3,4,5,6],[0,2,5],[0,1,2,3,4,5,6],[0,1,2,3,5,6]];\r\n\r\nx = list(str(x));\r\nif len(x) != k:\r\n\t# k 보다 자릿수 x가 자릿수가 작다면 작은 만큼 앞에 0을 채워주어야 한다.\r\n for _ in range(k-len(x)):\r\n x.insert(0,'0');\r\n \r\ndef dfs(index,count,st):\r\n global result;\r\n if count>p:\r\n return;\r\n if index == k:\r\n if 0 0:\r\n time += w_count*(times[i-1] + times[i]) + times[i];\r\n else:\r\n time += times[i];\r\n if time>limit:\r\n l = level+1;\r\n else:\r\n r = level;\r\n answer = level;\r\n return answer;\r\n ```\r\n\r\n## 느낀 점\r\n한 동안 알고리즘 문제를 안풀었더니 뇌가 굳은게 아닌가 싶다. 그리고 문제 설명 자체가 길고 복잡하면 살짝 길을 잃는 습관이 있는데, 이번 문제에서도 이해해보면 그렇게 어렵지 않은 것 같음에도 불구하고 그런 습관이 나타난 것 같다. \r\n\r\n전에 백준에서 풀었던 이분탐색문제랑 거의 흡사했음에도 이분탐색을 빨리 떠올리지 못한게 아쉽다."}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/lv3.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/lv3.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/lv3.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/tag/lv3.json diff --git a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/react.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/react.json similarity index 100% rename from _next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/react.json rename to _next/data/pwbOjWHyyP97zls4vVRix/posts/tag/react.json diff --git a/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/useState.json b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/useState.json new file mode 100644 index 0000000..2b24572 --- /dev/null +++ b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/useState.json @@ -0,0 +1 @@ +{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs"],"currentTag":"useState","posts":[{"slug":"posts/React/React useState 훅과 클로저","title":"React useState 훅과 클로저","date":"2024-10-26T18:00:24.000Z","image":"index.png","summary":"클로저를 이용한 React useState 훅의 작동 원리를 이해해보자","tags":["react","JavaScript","deepdive","useState","hook","클로저"],"content":"# React useState 훅과 클로저\r\n\r\n클로저에 대해 개념은 알고 있었지만, 사실 면접 질문때나 쓰이는 것이고, 실제로 어떻게 쓰이는지에 대해서는 와닿지 못한 부분도 있었다. 그러던 도중 react deep dive를 공부하며, react 개발자가 아마도 가장 많이 사용하는 **useState**에 클로저가 쓰인다는 사실을 알았다! \r\n어떻게 쓰이는 지 함께 살펴보자.\r\n\r\n\r\n\r\n## 상태값을 어떻게 관리할까\r\n보통 useState의 기본 사용법은 다음과 같을 것이다\r\n```js\r\nimport { useState } from 'react'\r\n\r\nconst [state, setState] = useState\r\n```\r\n인수로 state의 초깃값을 넘겨주고, 만일 아무것도 안넘겨주면 초깃값은 undefined일 것이다.\r\n훅의 반환 값은 배열이고, 배열의 첫 번째 원소는 state 값 자체이며, 두 번째 원소는 setState 함수를 통해 해당 state 값을 변경할 수 있다.\r\n\r\n\r\n**리액트에서 렌더링은 함수 컴포넌트의 return을 실행한 다음, 실행 결과를 이전의 리액트 트리와 비교해 리렌더링이 필요한 부분만 업데이트해 이뤄진다.**\r\n그렇기 때문에 렌더링 방식이랑 메커니즘이 다른 **변수**를 통해서 상태값을 관리하는 것은 적절하지 못하다. (이전 글 react virtualDOM 참고)\r\n\r\n그렇다면 다음 코드를 살펴보자.\r\n\r\n```js\r\nimport React from 'react'\r\n\r\nconst Component = () => {\r\n const [,triggerRender] = useState()\r\n let state = 'hello'\r\n\r\n function handleButtonClick() {\r\n state = 'hi'\r\n triggerRender()\r\n }\r\n return (\r\n <>\r\n

{state}

\r\n \r\n \r\n )\r\n}\r\n```\r\nuseState 반환값의 두 번째 원소를 실행해 리액트에 렌더링이 일어나게끔 변경했다. \r\n그럼에도 여전히 버튼 클릭시 state의 변경된 값이 렌더링되고 있지 않다. \r\n\r\n그 이유는 리액트의 렌더링은 **함수 컴포넌트에서 반환한 결과물인 return의 값을 비교해 실행되기 때문이다.** \r\n\r\n즉, 매번 렌더링이 발생할 때마다 함수는 새롭게 실행이 되고, 실행한 함수에서 state는 매번 hello로 초기화 되므로 아무리 state를 변경해도 hello로 초기화 되는 것이다. \r\n\r\n근데 렌더링이 될 때마다 초기화되는 변수(값)와는 달리, useState의 결과값은 어떻게 그 값을 유지할까?\r\n\r\n\r\n\r\n그럼 우리가 알고있는 useState는 대체 어떻게 구현이 되있는 것일지 한번 최대한 비슷하게 구현한 코드를 살펴보자.\r\n\r\n**먼저 useState의 결과 값이 유지되도록, state를 함수로 하여 state 값을 호출할 때마다 현재 state를 반환하게 해보자.**\r\n```js\r\nfunction useState(initialValue) {\r\n\tlet initialState = initialValue;\r\n\t\r\n\tfunction state() {\r\n\t\treturn initialState\r\n\t}\r\n\tfunction setState(newValue) {\r\n\t\tinitialState = newValue\r\n\t}\r\n\treturn [state, setState];\r\n}\r\nconst [value, setState] = useState(0);\r\nsetValue(1);\r\nconsole.log(value()); // 1\r\n```\r\n위의 코드도 나쁘진 않지만, 우리에게 익숙한 useState훅은 state를 함수가 아닌 상수처럼 사용하고 있다.\r\n어떻게 그게 가능한 걸까?\r\n\r\n## 클로저를 이용해 상태를 관리하는 useState\r\n이를 위해서 리액트는 **클로저**를 이용한 것이다. \r\nuseState는 클로저를 통해 useState 내부의 선언된 함수(setState)가 함수의 실행이 종료된 이후(useState가 호출된 이후)에도 지역변수인 state를 계속 참조할 수 있다.\r\n\r\nuseState 작동 방식을 대략적으로 흉내 낸 코드는 다음과 같다.\r\n\r\n```js\r\nconst MyReact = (function() {\r\n\tconst global = {}\r\n let index = 0\r\n \r\n function useState(initialState){\r\n \tif(!global.states) {\r\n \t// 애플리케이션 전체의 states 배열 초기화, 최초 접근이면 빈 배열로\r\n \tglobal.states = []\r\n }\r\n // states 정보를 조회해서, 현재 상태값이 있는지 확인\r\n // 없다면 초깃값으로 설정\r\n const currentState = global.states[index] || initialState\r\n // 위에서 조회한 값으로 states의 값 업데이트\r\n global.states[index] = currentState\r\n \r\n // 즉시실행함수로 setter 만듬\r\n const setState = (function() {\r\n \t// 클로저로 index를 가둬두어서 동일한 index에 접근이 가능\r\n \tlet currentIndex = index\r\n return function(value){\r\n \tglobal.states[currentIndex] = value\r\n //컴포넌트 렌더링이 들어가는 부분이다.(실제 코드는 생략)\r\n }\r\n })()\r\n // useState를 쓸 때마다 index를 하나씩 추가하는데, 이는 하나의 state마다\r\n // index가 할당되어있어, 그 index가 배열의 값(global.states)를 가리키고,\r\n // 필요할 때마다 그 값을 가져오게 하는 것이다.\r\n index = index + 1\r\n \r\n return [currentState,setState]\r\n}\r\n\r\nfunction Component() {\r\n\tconst [value, setValue] = useState(0);\r\n}\r\n})();\r\n```\r\n\r\n실제 리액트 코드에서는 useReducer를 이용해 구현되어 있어 약간의 차이가 있다.\r\n\r\n아무튼 여기서 함수의 실행이 끝났음에도 함수가 선언된 환경을 기억할 수 있는 방법이 바로 클로저인 것이다. 만약 클로저가 없다면, `setState`는 항상 `index`의 현재 값에 의존하게 된다. 즉, 컴포넌트가 여러 상태를 갖고 있을 때 마지막 `index`만 참조하므로, `setState`가 올바른 위치를 참조하지 않게 되는 것이다.\r\n\r\n매번 실행되는 함수 컴포넌트 환경에서 state의 값을 유지하고 사용하기 위해 리액트는 클로저를 활용하고 있다.\r\n\r\n`\r\n훅에 대한 구현체를 github에서 타고 올라가다보면 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 라는 문구를 만나게된다(무섭다 ㅋㅋ)\r\n위의 코드는 Preact의 구현을 기준으로 하고 있다. Preact는 react의 경량화 버전으로,\r\n대부분의 리액트 API를 지원하고 있다. \r\n`\r\n## 결론\r\nReact의 `useState`는 클로저를 통해 상태값을 안정적으로 유지하며, 함수 컴포넌트가 여러 번 호출되더라도 **각 상태값이 고유한 위치에 저장**될 수 있게 한다. `useState`가 반환하는 `setState` 함수는 생성 당시의 상태 위치(`index`)를 클로저로 캡처하여, 해당 상태값만 정확히 업데이트하도록 구현되어 있다.\r\n\r\n정리하자면, 클로저는 `setState`가 함수가 선언된 당시의 환경을 유지하게 해주기 때문에 **컴포넌트가 매번 재실행될 때마다 상태가 초기화되는 것을 방지**하고, 상태가 올바르게 유지될 수 있게 해준다.\r\n\r\n**참고**\r\n\r\n[서적] 모던 리액트 Deep Dive\r\n"}]},"__N_SSG":true} \ No newline at end of file diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\353\260\261\354\244\200.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\353\260\261\354\244\200.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\353\260\261\354\244\200.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\353\260\261\354\244\200.json" diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\354\203\235\353\252\205\354\243\274\352\270\260.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\354\203\235\353\252\205\354\243\274\352\270\260.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\354\203\235\353\252\205\354\243\274\352\270\260.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\354\203\235\353\252\205\354\243\274\352\270\260.json" diff --git "a/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.json" new file mode 100644 index 0000000..31f641a --- /dev/null +++ "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.json" @@ -0,0 +1 @@ +{"pageProps":{"tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색","react","JavaScript","deepdive","useState","hook","클로저","생명주기","Virtual DOM","백준","gold","dfs","lv3","bfs"],"currentTag":"이분탐색","posts":[{"slug":"posts/Algorithm/[PCCP 기출문제]퍼즐게임 챌린지","title":"[PCCP 기출문제] 퍼즐게임 챌린지","date":"2024-10-31T12:05:24.000Z","image":"index.jpg","summary":"프로그래머스 [PCCP 기출문제] level 2 퍼즐게임 챌린지 문제 풀이 입니다.","tags":["Algorithm","프로그래머스","PCCP","lv2","이분탐색"],"content":"# [PCCP 기출문제] 퍼즐게임 챌린지\r\n\r\n## 문제 요약\r\nhttps://school.programmers.co.kr/learn/courses/30/lessons/340212\r\n\r\n숙련도에 따라 퍼즐을 풀 때, \r\n- 각 퍼즐에서 난이도가 숙련도 보다 높다면 **난이도 - 숙련도 만큼 퍼즐이 틀리고, 틀린횟수 * (이전시간 + 현재시간)만큼의 시간이 걸린다.**\r\n- 각 퍼즐에서 난이도가 숙련도 보다 낮거나 같다면 **현재시간만** 투자하면 된다.\r\n\r\n이렇게 모든 퍼즐을 푼 시간의 합이 주어진 limit를 넘지 않도록 하는 숙련도(level)의 최솟값을 구하여야한다. \r\n\r\n(자세한 문제의 내용은 링크를 참고바랍니다.)\r\n\r\n## 문제 접근\r\n레벨의 최솟값을 구하여야 하기에, 처음에는 diffs 난이도 배열에서 최댓값부터 시작하여 1씩 줄어들도록 푸는 브루트포스를 생각했다.\r\n\r\n하지만, 제한사항이 1<=diffs[i]<=100000로 범위가 상당히 크고, diffs의 길이도 1 ≤ diffs의 길이 = times의 길이 = n ≤ 300,000로 제한사항이 주어졌기에\r\n\r\n브루트포스 기법은 무조건 시간초과가 날 것이므로 아닌 다른 방식을 생각해내야한다. \r\n\r\n결국 level의 값을 효율적이고 시간이 적게 들 수 있게 탐색해내야 하므로, O(nlogn)의 시간 복잡도를 가지는 **이분탐색으로 level의 최솟값을 구하면 된다.**\r\n\r\n## 풀이\r\n최대 난이도 이상의 값을 탐색하는 것은 의미가 없으므로, \r\n난이도의 최댓값을 구하여, max_diff로 놓은다음 이를 초기에 r로 설정한다.\r\nl은 난이도의 최솟값인 1로 두고,\r\n\r\n1~max_diff 범위 안에서 레벨의 이분탐색을 진행한다.\r\n\r\n현재 레벨을 변수 `level`로 둘 때,\r\n\r\n각 `level`에서 나올 수 있는 경우는 퍼즐을 푸는 시간의 합이 `limit`보다 **높을 때**와 **낮거나 같을 때**로 나눌 수 있다.\r\n- 시간의 합이 `limit`보다 높을 때: 현재 레벨이 낮기에 그런 것 이므로, 더 높은 레벨을 탐색하기 위해 \r\n`l = level+1` 로 둔다.\r\n\r\n- 시간의 합이 `limit` 보다 낮거나 같을 때: 현재 레벨이 충분히 높기에, 우리는 레벨의 최솟값을 찾아내야 하므로, \r\n`r = level-1`로 두고, 현재 레벨이 답이 될 수도 있기에 `answer = level` 도 추가해야 한다.\r\n\r\n이를 전체 코드로 구현하면 다음과 같다.\r\n\r\n## 전체 코드\r\n```js\r\n def solution(diffs, times, limit):\r\n max_diff = max(diffs)\r\n l = 1\r\n r = max_diff;\r\n answer = max_diff;\r\n while l 0:\r\n time += w_count*(times[i-1] + times[i]) + times[i];\r\n else:\r\n time += times[i];\r\n if time>limit:\r\n l = level+1;\r\n else:\r\n r = level;\r\n answer = level;\r\n return answer;\r\n ```\r\n\r\n## 느낀 점\r\n한 동안 알고리즘 문제를 안풀었더니 뇌가 굳은게 아닌가 싶다. 그리고 문제 설명 자체가 길고 복잡하면 살짝 길을 잃는 습관이 있는데, 이번 문제에서도 이해해보면 그렇게 어렵지 않은 것 같음에도 불구하고 그런 습관이 나타난 것 같다. \r\n\r\n전에 백준에서 풀었던 이분탐색문제랑 거의 흡사했음에도 이분탐색을 빨리 떠올리지 못한게 아쉽다."},{"slug":"posts/Algorithm/[프로그래머스lv3]징검다리건너기","title":"[프로그래머스 level 3] 징검다리 건너기","date":"2024-07-08T18:33:24.000Z","image":"index.png","summary":"이분탐색을 활용한 프로그래머스 [level 3] 징검다리 건너기 문제 풀이 입니다.","tags":["Algorithm","프로그래머스","lv3","이분탐색"],"content":"# [level 3] 징검다리 건너기 - 64062\r\n\r\n## 문제 설명\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/64062.%E2%80%85%EC%A7%95%EA%B2%80%EB%8B%A4%EB%A6%AC%E2%80%85%EA%B1%B4%EB%84%88%EA%B8%B0/README.md#%EB%AC%B8%EC%A0%9C-%EC%84%A4%EB%AA%85)\r\n\r\n**[본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.]**\r\n\r\n카카오 초등학교의 \"니니즈 친구들\"이 \"라이언\" 선생님과 함께 가을 소풍을 가는 중에 **징검다리**가 있는 개울을 만나서 건너편으로 건너려고 합니다. \"라이언\" 선생님은 \"니니즈 친구들\"이 무사히 징검다리를 건널 수 있도록 다음과 같이 규칙을 만들었습니다.\r\n\r\n- 징검다리는 일렬로 놓여 있고 각 징검다리의 디딤돌에는 모두 숫자가 적혀 있으며 디딤돌의 숫자는 한 번 밟을 때마다 1씩 줄어듭니다.\r\n- 디딤돌의 숫자가 0이 되면 더 이상 밟을 수 없으며 이때는 그 다음 디딤돌로 한번에 여러 칸을 건너 뛸 수 있습니다.\r\n- 단, 다음으로 밟을 수 있는 디딤돌이 여러 개인 경우 무조건 가장 가까운 디딤돌로만 건너뛸 수 있습니다.\r\n\r\n\"니니즈 친구들\"은 개울의 왼쪽에 있으며, 개울의 오른쪽 건너편에 도착해야 징검다리를 건넌 것으로 인정합니다. \r\n\"니니즈 친구들\"은 한 번에 한 명씩 징검다리를 건너야 하며, 한 친구가 징검다리를 모두 건넌 후에 그 다음 친구가 건너기 시작합니다.\r\n\r\n디딤돌에 적힌 숫자가 순서대로 담긴 배열 stones와 한 번에 건너뛸 수 있는 디딤돌의 최대 칸수 k가 매개변수로 주어질 때, 최대 몇 명까지 징검다리를 건널 수 있는지 return 하도록 solution 함수를 완성해주세요.\r\n\r\n#### [제한사항]\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/64062.%E2%80%85%EC%A7%95%EA%B2%80%EB%8B%A4%EB%A6%AC%E2%80%85%EA%B1%B4%EB%84%88%EA%B8%B0/README.md#%EC%A0%9C%ED%95%9C%EC%82%AC%ED%95%AD)\r\n\r\n- 징검다리를 건너야 하는 니니즈 친구들의 수는 무제한 이라고 간주합니다.\r\n- stones 배열의 크기는 1 이상 200,000 이하입니다.\r\n- stones 배열 각 원소들의 값은 1 이상 200,000,000 이하인 자연수입니다.\r\n- k는 1 이상 stones의 길이 이하인 자연수입니다.\r\n\r\n----------\r\n\r\n#### [입출력 예]\r\n\r\n\r\n\r\n \r\n\r\n\r\n\r\n\r\n\r\n \r\n\r\n\r\n\r\n\r\n\r\n
stoneskresult
[2, 4, 5, 3, 2, 1, 4, 2, 5, 1]33
\r\n\r\n##### **입출력 예에 대한 설명**\r\n\r\n[](https://github.com/phnml1/CodingTest/blob/master/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/3/64062.%E2%80%85%EC%A7%95%EA%B2%80%EB%8B%A4%EB%A6%AC%E2%80%85%EA%B1%B4%EB%84%88%EA%B8%B0/README.md#%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%98%88%EC%97%90-%EB%8C%80%ED%95%9C-%EC%84%A4%EB%AA%85)\r\n\r\n----------\r\n\r\n**입출력 예 #1**\r\n\r\n첫 번째 친구는 다음과 같이 징검다리를 건널 수 있습니다. \r\n![1.png](1.png)\r\n\r\n첫 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n두 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다. \r\n![2.png](2.png)\r\n\r\n두 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n세 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다. \r\n![3.png](3.png)\r\n\r\n세 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다. \r\n네 번째 친구가 징검다리를 건너려면, 세 번째 디딤돌에서 일곱 번째 디딤돌로 네 칸을 건너뛰어야 합니다. 하지만 k = 3 이므로 건너뛸 수 없습니다. \r\n![4.png](4.png)\r\n\r\n따라서 최대 3명이 디딤돌을 모두 건널 수 있습니다.\r\n\r\n## 접근 방법\r\n브루트 포스로 1명씩 건너기에는, stones 배열의 크기가 최대 200,000이고, stones 배열의 각 원소들의 값 역시 최대 200,000,000이기 때문에, 시간 초과가 날 확률이 높다고 생각했고(사실 그렇게 쉬우면 lv3 일리가 없지), 아니나 다를까 시간초과가 났다. \r\n\r\n고민하다가 도저히 해결책이 안나와서 찾아보니 `이분탐색`을 사용하여 풀 수 있었다.\r\n\r\n- 건널 수 있는 인원의 최소는 1명, 최대는 max(stones)명이다. 모든 돌들의 합이 같거나, 다르다 하더라도 건널수 없는 곳이 k이하면 건널 수 있기 때문이다. \r\n- 따라서 start = 1 end = max(stones)로 이분탐색을 시작할 수 있다.\r\n\r\nmid 값만큼의 사람이 건널 수 있는지 확인하기 위한 check함수는 다음과 같다.\r\n```python\r\ndef check(stones, mid,k):\r\n cnt = 0;\r\n for stone in stones:\r\n\t # 밟을 수 없으므로 건너뛰어야하는 횟수를 늘린다.\r\n if (stone - mid) <= 0:\r\n cnt += 1;\r\n # 밟을 수 있으므로 최대 건너뛰는 횟수를 초기화시킨다.\r\n else:\r\n cnt = 0;\r\n # 건너뛰는 횟수가 k보다 크거나 같으면 안되므로 False 리턴 \r\n if cnt>=k:\r\n return False;\r\n return True;\r\n```\r\n건널 수 있다면 mid보다 큰 범위에서 값을 찾아야하므로 start를 mid+1로 갱신하고, 반대라면, mid보다 작은 범위에서 값을 찾아야하므로 end를 mid-1로 갱신한다.\r\n \r\ncheck 시간 복잡도는 O(N)이고 이분탐색을 했을 때 O(logN)의 시간복잡도가 나오므로, 총 O(NlogN)의 시간 복잡도가 나오므로, 시간초과가 나지 않는다.\r\n\r\n전체 코드는 다음과 같다.\r\n```python\r\ndef check(stones, mid,k):\r\n cnt = 0;\r\n for stone in stones:\r\n if (stone - mid) <= 0:\r\n cnt += 1;\r\n else:\r\n cnt = 0;\r\n if cnt>=k:\r\n return False;\r\n return True;\r\n\r\ndef solution(stones, k):\r\n start,end = 1,max(stones);\r\n answer = 0;\r\n while start<=end:\r\n mid = (start + end) // 2;\r\n if check(stones, mid,k):\r\n start = mid+1;\r\n else:\r\n answer = mid;\r\n end = mid-1; \r\n return answer;\r\n```\r\n\r\n## 느낀 점\r\n이분탐색 문제를 많이는 아니더라도 어느정도는 풀어봤다고 생각했는데, 이 문제에서 이분탐색으로 푸는 아이디어를 떠올리지 못해서 아쉽다. \r\n\r\n또한 처음에 위에 코드의 6번째줄에서 else문을 쓰지않고, 이러한 코드로 제출하여서 시간초과가 났다.\r\n```python\r\nif (stone - mid) <= 0:\r\n\tcnt += 1;\r\nif (stone - mid)>0:\r\n\tcnt = 0;\r\n```\r\nㅎㅎ.. 사실 위와 같은 경우에서 if else문을 쓰는 것이 기본인데, 그래도 시간초과가 날 수 있는 직접적인 원인까지는 되지 않을 거라고 안일하게 생각해서 코드를 짠 게 화근이었다. 기본을 지키자!"}]},"__N_SSG":true} \ No newline at end of file diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\355\201\264\353\241\234\354\240\200.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\355\201\264\353\241\234\354\240\200.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\355\201\264\353\241\234\354\240\200.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\355\201\264\353\241\234\354\240\200.json" diff --git "a/_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244.json" "b/_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244.json" similarity index 100% rename from "_next/data/kbtYb12H_OTxS29UAs1xe/posts/tag/\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244.json" rename to "_next/data/pwbOjWHyyP97zls4vVRix/posts/tag/\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244.json" diff --git a/_next/static/chunks/pages/[...detail]-c311debfc124ca6f.js b/_next/static/chunks/pages/[...detail]-83887a767c3729b9.js similarity index 94% rename from _next/static/chunks/pages/[...detail]-c311debfc124ca6f.js rename to _next/static/chunks/pages/[...detail]-83887a767c3729b9.js index 4f38bc4..e628265 100644 --- a/_next/static/chunks/pages/[...detail]-c311debfc124ca6f.js +++ b/_next/static/chunks/pages/[...detail]-83887a767c3729b9.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[492],{6069:function(e,t,r){(window.__NEXT_P=window.__NEXT_P||[]).push(["/[...detail]",function(){return r(17)}])},588:function(e,t,r){"use strict";var s=r(5893),a=r(1664),l=r.n(a);t.Z=e=>(0,s.jsx)(l(),{href:"/posts/tag/".concat(e.name),className:"w-fit h-fit px-2 py-1 transition-all bg-gray-100 rounded-lg md:text-sm sm:text-xs dark:bg-dark-secondary dark:hover:text-white hover:bg-tag-hover dark:hover:bg-tag-dark-hover",children:e.name})},17:function(e,t,r){"use strict";r.r(t),r.d(t,{PostDetailPage:function(){return er},__N_SSG:function(){return et},default:function(){return es}});var s,a,l,n,o=r(5893),i=r(8080),c=r(7294),d=()=>{let[e,t]=(0,c.useState)(0),[r,s]=(0,c.useState)(0),a=(0,c.useCallback)(()=>{let e=window.scrollY;s(e);let r=document.documentElement,a=r.scrollTop||document.body.scrollTop,l=r.scrollHeight||document.body.scrollHeight,n=a/(l-r.clientHeight)*100;t(n)},[r]);return(0,c.useEffect)(()=>(window.addEventListener("scroll",a),()=>{window.removeEventListener("scroll",a)}),[a]),(0,o.jsx)("div",{className:"fixed z-50 top-1 transtion-all -mt-1 left-0 h-1 bg-gradient-to-r from-sky-500 to-indigo-500",style:{width:"".concat(e,"%")}})},u=r(1578),h=r(1664),p=r.n(h),g=e=>(0,o.jsx)(p(),{href:"/posts/".concat(e.text),className:"w-auto px-4 border-indigo-600 text-indigo-600 dark:border-indigo-200 dark:text-indigo-200 border-2 border-solid rounded-lg py-1 h-8 flex justify-center items-center cursor-pointer",children:e.text}),m=r(588),x=r(9727);function f(){return(f=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let t=(0,u.ZP)(new Date(e.date),"yyyy년 MM월 dd일");return(0,o.jsxs)("div",{className:"w-full flex flex-col items-center mt-6",children:[(0,o.jsx)(g,{text:e.category}),(0,o.jsx)("div",{className:"font-bold text-3xl md:text-5xl mt-2 text-center leading-snug md:leading-snug",children:e.title}),(0,o.jsx)("div",{className:"flex gap-4 mt-4 break-words flex-wrap w-full justify-center",children:e.tags.map(e=>(0,o.jsx)(m.Z,{name:e},e))}),(0,o.jsx)("div",{className:"text-md mt-4",children:t}),(0,o.jsxs)("div",{className:"flex text-sm mt-2 items-center gap-1 text-neutral-600",children:[(0,o.jsx)(v,{width:20,height:20}),(0,o.jsxs)("span",{children:[e.readingTime,"분"]})]}),(0,o.jsx)(x.Z,{mt:"6"})]})},j=r(5675),w=r.n(j),y=r(8152),k=r(4283),N=r(2041),D=r(902),E=r(2422);let O={threshold:.5,rootMargin:"-70px 0px -60% 0px"};var T=(e,t)=>{let r=(0,c.useRef)({}),s=(0,c.useCallback)(s=>{r.current={},r.current=s.reduce((e,t)=>(e[t.target.id]=t,e),r.current);let a=[];Object.keys(r.current).forEach(e=>{let t=r.current[e];t.isIntersecting&&a.push(t)});let l=e=>t.findIndex(t=>t.id===e);if(1===a.length)e(a[0].target.id);else if(a.length>1){let t=a.sort((e,t)=>l(e.target.id)-l(t.target.id));e(t[0].target.id)}},[t]);(0,c.useEffect)(()=>{let e=new IntersectionObserver(s,O);return t.map(t=>{e.observe(t)}),()=>{e.disconnect()}},[t])},_=r(6261);function P(){return(P=Object.assign?Object.assign.bind():function(e){for(var t=1;t(0,o.jsx)(p(),{href:"/posts/".concat(e.category),className:"w-auto h-auto fill-[#666666] dark:fill-[rgb(181,181,181)] rounded-lg hover:bg-slate-200 dark:hover:bg-slate-800 p-2 transition-colors cursor-pointer text-neutral-700",children:(0,o.jsx)(M,{})}),H=r(1163),S=r.n(H),L=e=>{let t=(0,c.useRef)(null),r=(0,H.useRouter)(),[s,a]=(0,c.useState)(""),[l,n]=(0,c.useState)([]);return console.log(s),(0,c.useEffect)(()=>{let e=Array.from(document.querySelectorAll("h1,h2,h3"));n(e),a("")},[e.slug]),(0,c.useEffect)(()=>{var e;let r=null===(e=t.current)||void 0===e?void 0:e.querySelector(".font-bold.text-indigo-500");if(r&&t.current){let e=t.current.getBoundingClientRect().top,s=r.getBoundingClientRect().top;t.current.scrollTop+=s-e}},[s]),T(a,l),(0,o.jsx)("div",{className:"mt-12 ml-auto relative hidden lg:block",children:(0,o.jsxs)("div",{className:"sticky top-32 w-60 ",children:[(0,o.jsxs)("div",{className:"p-4 max-h-[500px] rounded-t-xl border-solid border-slate-200 border-[0.5px] border-b-0 dark:border-gray-600 items-start gap-6",children:[(0,o.jsx)("div",{className:"font-bold mb-2",children:e.title}),(0,o.jsx)("hr",{className:"w-full border-neutral-400 dark:border-gray-600"}),(0,o.jsx)("div",{ref:t,className:"custom-scroll overflow-y-auto max-h-[380px] mt-2 mb-2 px-2",children:(0,o.jsx)("ul",{className:"mb-2 text-sm flex flex-col gap-2 mt-4 ",children:l.map((e,t)=>{let r=e.nodeName,a=e.innerHTML,l=e.id,n=s==l?"font-bold text-indigo-500":"";return"H1"===r?(0,o.jsx)("li",{className:"".concat(n),children:(0,o.jsx)(_.rU,{to:"".concat(l),spy:!0,smooth:!0,duration:400,className:"".concat(n," py-1 cursor-pointer transition-colors"),offset:-100,children:a})},t):"H2"===r?(0,o.jsx)("li",{children:(0,o.jsx)(_.rU,{to:"".concat(l),spy:!0,smooth:!0,duration:400,offset:-100,className:"".concat(n," py-1 pl-2 whitespace-pre-line cursor-pointer transition-colors block"),children:a})},t):(0,o.jsx)("li",{children:(0,o.jsx)(_.rU,{to:"".concat(l),spy:!0,smooth:!0,duration:400,offset:-50,className:"".concat(n," py-1 pl-6 whitespace-pre-line cursor-pointer transition-colors block"),children:a})},t)})})})]}),(0,o.jsxs)("div",{className:"rounded-b-xl border-[0.5px] px-6 w-full h-12 border-solid bg-slate-100 dark:bg-[#363636] dark:border-gray-600 flex items-center justify-between",children:[(0,o.jsx)(z,{category:r.query.detail[1]}),(0,o.jsx)(Z,{onClick:()=>{_.NY.scrollToTop()},className:"w-auto h-auto rounded-lg hover:bg-slate-200 p-2 cursor-pointer text-white transition-colors dark:hover:bg-gray-800 fill-[#666666] dark:fill-[rgb(181,181,181)]"}),(0,o.jsx)(_.rU,{to:"giscus",spy:!0,smooth:!0,duration:400,children:(0,o.jsx)(A,{className:"w-auto h-auto rounded-lg p-2 hover:bg-slate-200 cursor-pointer text-neutral-700 transition-colors dark:hover:bg-slate-800 fill-[#666666] dark:fill-[rgb(181,181,181)]"})})]})]})})},R=r(9943),B=r(7896),U=r(7388);y.Z.registerLanguage("js",N.Z),y.Z.registerLanguage("css",E.Z),y.Z.registerLanguage("python",D.Z);var I=e=>(0,o.jsx)("div",{className:"w-full flex mt-8 mb-8",children:(0,o.jsxs)("div",{className:"w-full gap-8 lg:flex",children:[(0,o.jsx)("div",{className:"prose prose-zinc w-full leading-loose max-w-3xl dark:prose-invert",children:(0,o.jsx)(R.U,{rehypePlugins:[B.Z,U.Z],components:{a:e=>{let{href:t,children:r}=e;return(0,o.jsx)(p(),{href:t,children:r})},pre:e=>{if(e.children.props.className){var t;let r=null===(t=e.children.props.className)||void 0===t?void 0:t.split("-")[1];return(0,o.jsx)(y.Z,{style:k.pJ,PreTag:"pre",language:r,CodeTag:e=>{let{children:t}=e;return(0,o.jsx)("code",{className:"whitespace-pre-wrap",children:t})},children:e.children.props.children})}},img:t=>{console.log(t);let r=t.width?t.width:"600",s=t.height?t.height:"300";return(0,o.jsx)(w(),{src:"/".concat(e.slug,"/").concat(t.src),alt:t.src,width:r,height:s})}},children:e.content})}),(0,o.jsx)(L,{title:e.title,slug:e.slug})]})}),F=r(7103),K=r(7138),W=r(6378),X=()=>(0,o.jsxs)("div",{className:"p-12 flex gap-4 justify-center items-center",children:[(0,o.jsx)("div",{children:(0,o.jsx)(w(),{src:"/profile/profile.png",alt:"my profile photo",width:80,height:80,className:"rounded-full"})}),(0,o.jsxs)("div",{className:"flex flex-col w-auto gap-2",children:[(0,o.jsxs)("div",{children:[(0,o.jsx)("div",{className:"w-full font-bold text-base mb-1",children:"Phnml1"}),(0,o.jsx)("div",{className:"w-full text-sm text-slate-500 dark:text-dark-secondary",children:"더디더라도 조금씩 성장하는 프론트엔드 개발자"})]}),(0,o.jsxs)("div",{className:"w-full flex gap-2",children:[(0,o.jsx)(p(),{href:"".concat(W.b.link),target:"_blank",children:(0,o.jsx)(F.Z,{width:"25",height:"25",className:"fill-slate-300 transition-all cursor-pointer hover:fill-black dark:hover:fill-white"})}),(0,o.jsx)(p(),{href:"mailto:juyung0903@gmail.com",target:"_blank",children:(0,o.jsx)(K.Z,{width:"25",height:"25",className:"fill-slate-300 transition-all cursor-pointer hover:fill-black dark:hover:fill-white"})})]})]})]}),Y=e=>(0,o.jsxs)("section",{className:"w-full flex-col flex gap-8 mt-20 mb-20 lg:flex-row lg:gap-52",children:["object"==typeof e.prevData&&(0,o.jsxs)(p(),{href:"".concat(e.prevData.slug),className:"w-full h-auto rounded-lg border-[1px] break-words border-detailnav cursor-pointer hover:scale-105 transition-all p-8 lg:w-[calc(50%-6.5rem)] dark:border-darkdetailnav",children:[(0,o.jsx)("div",{className:"text-sm mb-2",children:"이전글"}),(0,o.jsx)("div",{className:"font-bold mb-2 text-lg",children:e.prevData.title}),(0,o.jsx)("div",{className:"text-sm line-clamp-2 overflow-hidden overflow-ellipsis text-slate-600 dark:text-dark-secondary",children:e.prevData.summary})]}),"object"==typeof e.nextData&&(0,o.jsxs)(p(),{href:"".concat(e.nextData.slug),className:"w-full h-auto rounded-lg border-[1px] break-words border-detailnav cursor-pointer hover:scale-105 transition-all p-8 lg:w-[calc(50%-6.5rem)] dark:border-darkdetailnav",children:[(0,o.jsx)("div",{className:"text-sm mb-2",children:"다음글"}),(0,o.jsx)("div",{className:"font-bold mb-2 text-lg",children:e.nextData.title}),(0,o.jsx)("div",{className:"text-sm line-clamp-2 overflow-hidden overflow-ellipsis text-slate-600 dark:text-dark-secondary",children:e.nextData.summary})]})]}),G=r(2010);function J(){let e=(0,c.useRef)(null),{resolvedTheme:t}=(0,G.F)(),r="dark"===t?"dark":"light";return(0,c.useEffect)(()=>{if(!e.current||e.current.hasChildNodes())return;let t=document.createElement("script");t.src="https://giscus.app/client.js",t.async=!0,t.crossOrigin="anonymous",t.setAttribute("data-repo","phnml1/phnml1.github.io"),t.setAttribute("data-repo-id","R_kgDOKznePw"),t.setAttribute("data-category","Announcements"),t.setAttribute("data-category-id","DIC_kwDOKzneP84CcqiK"),t.setAttribute("data-mapping","pathname"),t.setAttribute("data-strict","0"),t.setAttribute("data-reactions-enabled","1"),t.setAttribute("data-emit-metadata","0"),t.setAttribute("data-input-position","bottom"),t.setAttribute("data-theme",r),t.setAttribute("data-lang","ko"),e.current.appendChild(t)},[]),(0,c.useEffect)(()=>{var e;let t=document.querySelector("iframe.giscus-frame");null==t||null===(e=t.contentWindow)||void 0===e||e.postMessage({giscus:{setConfig:{theme:r}}},"https://giscus.app")},[r]),(0,c.useEffect)(()=>{var e;let t=document.querySelector("iframe.giscus-frame");null==t||null===(e=t.contentWindow)||void 0===e||e.postMessage({giscus:{setConfig:{term:S().asPath}}},"https://giscus.app"),console.log(S().asPath)},[S().asPath]),(0,o.jsx)("section",{id:"giscus",ref:e})}var Q=e=>(0,o.jsxs)("div",{className:"w-full mt-12",children:[(0,o.jsx)(X,{}),(0,o.jsx)(Y,{prevData:e.prevData,nextData:e.nextData}),(0,o.jsx)(J,{})]});function V(e){return(0,o.jsx)(i.Z,{children:(0,o.jsxs)(c.Fragment,{children:[(0,o.jsx)(d,{}),(0,o.jsxs)("div",{className:"mt-4 w-full md:w-4/5 px-8 flex flex-col items-center mb-16",children:[(0,o.jsx)(b,{title:e.post.title,category:e.category,date:e.post.date,tags:e.post.tags,readingTime:e.post.readingMinutes}),(0,o.jsx)(I,{title:e.post.title,content:e.post.content,slug:e.post.slug}),(0,o.jsx)(x.Z,{mt:"8"}),(0,o.jsx)(Q,{prevData:e.prevData,nextData:e.nextData})]})]})})}var $=r(9008),ee=r.n($),et=!0;function er(e){return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsxs)(ee(),{children:[(0,o.jsx)("title",{children:e.post.title}),(0,o.jsx)("meta",{name:"description",content:"".concat(e.post.summary)}),(0,o.jsx)("meta",{property:"og:title",content:e.post.title}),(0,o.jsx)("meta",{property:"og:description",content:e.post.summary}),(0,o.jsx)("meta",{property:"og:image",content:"https://phnml1.github.io/".concat(e.post.slug,"/").concat(e.post.image)}),(0,o.jsx)("meta",{property:"og:url",content:"https://phnml1.github.io/".concat(e.post.slug)}),(0,o.jsx)("meta",{property:"og:type",content:"article"})]}),(0,o.jsx)(V,{post:e.post,category:e.category,prevData:e.prevData,nextData:e.nextData})]})}var es=er}},function(e){e.O(0,[815,135,393,979,774,888,179],function(){return e(e.s=6069)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[492],{6069:function(e,t,r){(window.__NEXT_P=window.__NEXT_P||[]).push(["/[...detail]",function(){return r(17)}])},588:function(e,t,r){"use strict";var s=r(5893),a=r(1664),l=r.n(a);t.Z=e=>(0,s.jsx)(l(),{href:"/posts/tag/".concat(e.name),className:"w-fit h-fit px-2 py-1 transition-all bg-gray-100 rounded-lg md:text-sm sm:text-xs dark:bg-dark-secondary dark:hover:text-white hover:bg-tag-hover dark:hover:bg-tag-dark-hover",children:e.name})},17:function(e,t,r){"use strict";r.r(t),r.d(t,{PostDetailPage:function(){return er},__N_SSG:function(){return et},default:function(){return es}});var s,a,l,n,o=r(5893),i=r(8080),c=r(7294),d=()=>{let[e,t]=(0,c.useState)(0),[r,s]=(0,c.useState)(0),a=(0,c.useCallback)(()=>{let e=window.scrollY;s(e);let r=document.documentElement,a=r.scrollTop||document.body.scrollTop,l=r.scrollHeight||document.body.scrollHeight,n=a/(l-r.clientHeight)*100;t(n)},[r]);return(0,c.useEffect)(()=>(window.addEventListener("scroll",a),()=>{window.removeEventListener("scroll",a)}),[a]),(0,o.jsx)("div",{className:"fixed z-50 top-1 transtion-all -mt-1 left-0 h-1 bg-gradient-to-r from-sky-500 to-indigo-500",style:{width:"".concat(e,"%")}})},u=r(1578),h=r(1664),p=r.n(h),g=e=>(0,o.jsx)(p(),{href:"/posts/".concat(e.text),className:"w-auto px-4 border-indigo-600 text-indigo-600 dark:border-indigo-200 dark:text-indigo-200 border-2 border-solid rounded-lg py-1 h-8 flex justify-center items-center cursor-pointer",children:e.text}),m=r(588),x=r(9727);function f(){return(f=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let t=(0,u.ZP)(new Date(e.date),"yyyy년 MM월 dd일");return(0,o.jsxs)("div",{className:"w-full flex flex-col items-center mt-6",children:[(0,o.jsx)(g,{text:e.category}),(0,o.jsx)("div",{className:"font-bold text-3xl md:text-5xl mt-2 text-center leading-snug md:leading-snug",children:e.title}),(0,o.jsx)("div",{className:"flex gap-4 mt-4 break-words flex-wrap w-full justify-center",children:e.tags.map(e=>(0,o.jsx)(m.Z,{name:e},e))}),(0,o.jsx)("div",{className:"text-md mt-4",children:t}),(0,o.jsxs)("div",{className:"flex text-sm mt-2 items-center gap-1 text-neutral-600",children:[(0,o.jsx)(v,{width:20,height:20}),(0,o.jsxs)("span",{children:[e.readingTime,"분"]})]}),(0,o.jsx)(x.Z,{mt:"6"})]})},j=r(5675),w=r.n(j),y=r(8152),k=r(4283),N=r(2041),D=r(902),E=r(2422);let O={threshold:.5,rootMargin:"-70px 0px -60% 0px"};var T=(e,t)=>{let r=(0,c.useRef)({}),s=(0,c.useCallback)(s=>{r.current={},r.current=s.reduce((e,t)=>(e[t.target.id]=t,e),r.current);let a=[];Object.keys(r.current).forEach(e=>{let t=r.current[e];t.isIntersecting&&a.push(t)});let l=e=>t.findIndex(t=>t.id===e);if(1===a.length)e(a[0].target.id);else if(a.length>1){let t=a.sort((e,t)=>l(e.target.id)-l(t.target.id));e(t[0].target.id)}},[t]);(0,c.useEffect)(()=>{let e=new IntersectionObserver(s,O);return t.map(t=>{e.observe(t)}),()=>{e.disconnect()}},[t])},_=r(6261);function P(){return(P=Object.assign?Object.assign.bind():function(e){for(var t=1;t(0,o.jsx)(p(),{href:"/posts/".concat(e.category),className:"w-auto h-auto fill-[#666666] dark:fill-[rgb(181,181,181)] rounded-lg hover:bg-slate-200 dark:hover:bg-slate-800 p-2 transition-colors cursor-pointer text-neutral-700",children:(0,o.jsx)(M,{})}),H=r(1163),S=r.n(H),L=e=>{let t=(0,c.useRef)(null),r=(0,H.useRouter)(),[s,a]=(0,c.useState)(""),[l,n]=(0,c.useState)([]);return console.log(s),(0,c.useEffect)(()=>{let e=Array.from(document.querySelectorAll("h1,h2,h3"));n(e),a("")},[e.slug]),(0,c.useEffect)(()=>{var e;let r=null===(e=t.current)||void 0===e?void 0:e.querySelector(".font-bold.text-indigo-500");if(r&&t.current){let e=t.current.getBoundingClientRect().top,s=r.getBoundingClientRect().top;t.current.scrollTop+=s-e}},[s]),T(a,l),(0,o.jsx)("div",{className:"mt-12 ml-auto relative hidden lg:block",children:(0,o.jsxs)("div",{className:"sticky top-32 w-60 ",children:[(0,o.jsxs)("div",{className:"p-4 max-h-[500px] rounded-t-xl border-solid border-slate-200 border-[0.5px] border-b-0 dark:border-gray-600 items-start gap-6",children:[(0,o.jsx)("div",{className:"font-bold mb-2",children:e.title}),(0,o.jsx)("hr",{className:"w-full border-neutral-400 dark:border-gray-600"}),(0,o.jsx)("div",{ref:t,className:"custom-scroll overflow-y-auto max-h-[380px] mt-2 mb-2 px-2",children:(0,o.jsx)("ul",{className:"mb-2 text-sm flex flex-col gap-2 mt-4 ",children:l.map((e,t)=>{let r=e.nodeName,a=e.innerHTML,l=e.id,n=s==l?"font-bold text-indigo-500":"";return"H1"===r?(0,o.jsx)("li",{className:"".concat(n),children:(0,o.jsx)(_.rU,{to:"".concat(l),spy:!0,smooth:!0,duration:400,className:"".concat(n," py-1 cursor-pointer transition-colors"),offset:-100,children:a})},t):"H2"===r?(0,o.jsx)("li",{children:(0,o.jsx)(_.rU,{to:"".concat(l),spy:!0,smooth:!0,duration:400,offset:-100,className:"".concat(n," py-1 pl-2 whitespace-pre-line cursor-pointer transition-colors block"),children:a})},t):(0,o.jsx)("li",{children:(0,o.jsx)(_.rU,{to:"".concat(l),spy:!0,smooth:!0,duration:400,offset:-50,className:"".concat(n," py-1 pl-6 whitespace-pre-line cursor-pointer transition-colors block"),children:a})},t)})})})]}),(0,o.jsxs)("div",{className:"rounded-b-xl border-[0.5px] px-6 w-full h-12 border-solid bg-slate-100 dark:bg-[#363636] dark:border-gray-600 flex items-center justify-between",children:[(0,o.jsx)(z,{category:r.query.detail[1]}),(0,o.jsx)(Z,{onClick:()=>{_.NY.scrollToTop()},className:"w-auto h-auto rounded-lg hover:bg-slate-200 p-2 cursor-pointer text-white transition-colors dark:hover:bg-gray-800 fill-[#666666] dark:fill-[rgb(181,181,181)]"}),(0,o.jsx)(_.rU,{to:"giscus",spy:!0,smooth:!0,duration:400,children:(0,o.jsx)(A,{className:"w-auto h-auto rounded-lg p-2 hover:bg-slate-200 cursor-pointer text-neutral-700 transition-colors dark:hover:bg-slate-800 fill-[#666666] dark:fill-[rgb(181,181,181)]"})})]})]})})},R=r(9943),B=r(7896),U=r(7388);y.Z.registerLanguage("js",N.Z),y.Z.registerLanguage("css",E.Z),y.Z.registerLanguage("python",D.Z);var I=e=>(0,o.jsx)("div",{className:"w-full flex mt-8 mb-8",children:(0,o.jsxs)("div",{className:"w-full gap-8 lg:flex",children:[(0,o.jsx)("div",{className:"prose prose-zinc w-full leading-loose max-w-3xl dark:prose-invert",children:(0,o.jsx)(R.U,{rehypePlugins:[B.Z,U.Z],components:{a:e=>{let{href:t,children:r}=e;return(0,o.jsx)(p(),{href:t,children:r})},pre:e=>{if(e.children.props.className){var t;let r=null===(t=e.children.props.className)||void 0===t?void 0:t.split("-")[1];return(0,o.jsx)(y.Z,{style:k.pJ,PreTag:"pre",language:r,CodeTag:e=>{let{children:t}=e;return(0,o.jsx)("code",{className:"whitespace-pre-wrap",children:t})},children:e.children.props.children})}},img:t=>{console.log(t);let r=t.width?t.width:"600",s=t.height?t.height:"300";return(0,o.jsx)(w(),{src:"/".concat(e.slug,"/").concat(t.src),alt:t.src,width:r,height:s})}},children:e.content})}),(0,o.jsx)(L,{title:e.title,slug:e.slug})]})}),F=r(7103),K=r(7138),W=r(6378),X=()=>(0,o.jsxs)("div",{className:"p-12 flex gap-4 justify-center items-center",children:[(0,o.jsx)("div",{children:(0,o.jsx)(w(),{src:"/profile/profile.png",alt:"my profile photo",width:80,height:80,className:"rounded-full"})}),(0,o.jsxs)("div",{className:"flex flex-col w-auto gap-2",children:[(0,o.jsxs)("div",{children:[(0,o.jsx)("div",{className:"w-full font-bold text-base mb-1",children:"Phnml1"}),(0,o.jsx)("div",{className:"w-full text-sm text-slate-500 dark:text-dark-secondary",children:"더디더라도 조금씩 성장하는 프론트엔드 개발자"})]}),(0,o.jsxs)("div",{className:"w-full flex gap-2",children:[(0,o.jsx)(p(),{href:"".concat(W.b.link),target:"_blank",children:(0,o.jsx)(F.Z,{width:"25",height:"25",className:"fill-slate-300 transition-all cursor-pointer hover:fill-black dark:hover:fill-white"})}),(0,o.jsx)(p(),{href:"mailto:juyung0903@gmail.com",target:"_blank",children:(0,o.jsx)(K.Z,{width:"25",height:"25",className:"fill-slate-300 transition-all cursor-pointer hover:fill-black dark:hover:fill-white"})})]})]})]}),Y=e=>(0,o.jsxs)("section",{className:"w-full flex-col flex gap-8 mt-20 mb-20 lg:flex-row lg:gap-52",children:["object"==typeof e.prevData&&(0,o.jsxs)(p(),{href:"".concat(e.prevData.slug),className:"w-full h-auto rounded-lg border-[1px] break-words border-detailnav cursor-pointer hover:scale-105 transition-all p-8 lg:w-[calc(50%-6.5rem)] dark:border-darkdetailnav",children:[(0,o.jsx)("div",{className:"text-sm mb-2",children:"이전글"}),(0,o.jsx)("div",{className:"font-bold mb-2 text-lg",children:e.prevData.title}),(0,o.jsx)("div",{className:"text-sm line-clamp-2 overflow-hidden overflow-ellipsis text-slate-600 dark:text-dark-secondary",children:e.prevData.summary})]}),"object"==typeof e.nextData&&(0,o.jsxs)(p(),{href:"".concat(e.nextData.slug),className:"w-full h-auto rounded-lg border-[1px] break-words border-detailnav cursor-pointer hover:scale-105 transition-all p-8 lg:w-[calc(50%-6.5rem)] dark:border-darkdetailnav",children:[(0,o.jsx)("div",{className:"text-sm mb-2",children:"다음글"}),(0,o.jsx)("div",{className:"font-bold mb-2 text-lg",children:e.nextData.title}),(0,o.jsx)("div",{className:"text-sm line-clamp-2 overflow-hidden overflow-ellipsis text-slate-600 dark:text-dark-secondary",children:e.nextData.summary})]})]}),G=r(2010);function J(){let e=(0,c.useRef)(null),{resolvedTheme:t}=(0,G.F)(),r="dark"===t?"dark":"light";return(0,c.useEffect)(()=>{if(!e.current||e.current.hasChildNodes())return;let t=document.createElement("script");t.src="https://giscus.app/client.js",t.async=!0,t.crossOrigin="anonymous",t.setAttribute("data-repo","phnml1/phnml1.github.io"),t.setAttribute("data-repo-id","R_kgDOKznePw"),t.setAttribute("data-category","Announcements"),t.setAttribute("data-category-id","DIC_kwDOKzneP84CcqiK"),t.setAttribute("data-mapping","pathname"),t.setAttribute("data-strict","0"),t.setAttribute("data-reactions-enabled","1"),t.setAttribute("data-emit-metadata","0"),t.setAttribute("data-input-position","bottom"),t.setAttribute("data-theme",r),t.setAttribute("data-lang","ko"),e.current.appendChild(t)},[]),(0,c.useEffect)(()=>{var e;let t=document.querySelector("iframe.giscus-frame");null==t||null===(e=t.contentWindow)||void 0===e||e.postMessage({giscus:{setConfig:{theme:r}}},"https://giscus.app")},[r]),(0,c.useEffect)(()=>{var e;let t=document.querySelector("iframe.giscus-frame");null==t||null===(e=t.contentWindow)||void 0===e||e.postMessage({giscus:{setConfig:{term:S().asPath}}},"https://giscus.app"),console.log(S().asPath)},[S().asPath]),(0,o.jsx)("section",{id:"giscus",ref:e})}var Q=e=>(0,o.jsxs)("div",{className:"w-full mt-12",children:[(0,o.jsx)(X,{}),(0,o.jsx)(Y,{prevData:e.prevData,nextData:e.nextData}),(0,o.jsx)(J,{})]});function V(e){return(0,o.jsx)(i.Z,{children:(0,o.jsxs)(c.Fragment,{children:[(0,o.jsx)(d,{}),(0,o.jsxs)("div",{className:"mt-4 w-full md:w-4/5 px-8 flex flex-col items-center mb-16",children:[(0,o.jsx)(b,{title:e.post.title,category:e.category,date:e.post.date,tags:e.post.tags,readingTime:e.post.readingMinutes}),(0,o.jsx)(I,{title:e.post.title,content:e.post.content,slug:e.post.slug}),(0,o.jsx)(x.Z,{mt:"8"}),(0,o.jsx)(Q,{prevData:e.prevData,nextData:e.nextData})]})]})})}var $=r(9008),ee=r.n($),et=!0;function er(e){return console.log(e.post.slug),(0,o.jsxs)(o.Fragment,{children:[(0,o.jsxs)(ee(),{children:[(0,o.jsx)("title",{children:e.post.title}),(0,o.jsx)("meta",{name:"description",content:"".concat(e.post.summary)}),(0,o.jsx)("meta",{property:"og:title",content:e.post.title}),(0,o.jsx)("meta",{property:"og:description",content:e.post.summary}),(0,o.jsx)("meta",{property:"og:image",content:"https://phnml1.github.io/".concat(e.post.slug,"/").concat(e.post.image)}),(0,o.jsx)("meta",{property:"og:url",content:"https://phnml1.github.io/".concat(e.post.slug)}),(0,o.jsx)("meta",{property:"og:type",content:"article"})]}),(0,o.jsx)(V,{post:e.post,category:e.category,prevData:e.prevData,nextData:e.nextData})]})}var es=er}},function(e){e.O(0,[815,135,393,979,774,888,179],function(){return e(e.s=6069)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/kbtYb12H_OTxS29UAs1xe/_buildManifest.js b/_next/static/pwbOjWHyyP97zls4vVRix/_buildManifest.js similarity index 92% rename from _next/static/kbtYb12H_OTxS29UAs1xe/_buildManifest.js rename to _next/static/pwbOjWHyyP97zls4vVRix/_buildManifest.js index 6f3840e..f842314 100644 --- a/_next/static/kbtYb12H_OTxS29UAs1xe/_buildManifest.js +++ b/_next/static/pwbOjWHyyP97zls4vVRix/_buildManifest.js @@ -1 +1 @@ -self.__BUILD_MANIFEST=function(s,c,t,a){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[s,c,t,a,"static/chunks/pages/index-29ef57cf887c10bb.js"],"/_error":["static/chunks/pages/_error-ee5b5fb91d29d86f.js"],"/posts/search":[s,c,t,a,"static/chunks/pages/posts/search-369a7036ab19d85c.js"],"/posts/tag/[tag]":[s,c,t,a,"static/chunks/pages/posts/tag/[tag]-9c29e7860c712dac.js"],"/posts/[category]":[s,c,t,a,"static/chunks/pages/posts/[category]-731f2768a845326a.js"],"/[...detail]":[s,c,t,"static/chunks/393-7217afb82985f4a5.js",a,"static/chunks/pages/[...detail]-c311debfc124ca6f.js"],sortedPages:["/","/_app","/_error","/posts/search","/posts/tag/[tag]","/posts/[category]","/[...detail]"]}}("static/css/570acc098c248c2f.css","static/css/0407c34f82b3c3db.css","static/chunks/135-4ea70e0295549ad8.js","static/chunks/979-7390dd4fc9e19803.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file +self.__BUILD_MANIFEST=function(s,c,t,a){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[s,c,t,a,"static/chunks/pages/index-29ef57cf887c10bb.js"],"/_error":["static/chunks/pages/_error-ee5b5fb91d29d86f.js"],"/posts/search":[s,c,t,a,"static/chunks/pages/posts/search-369a7036ab19d85c.js"],"/posts/tag/[tag]":[s,c,t,a,"static/chunks/pages/posts/tag/[tag]-9c29e7860c712dac.js"],"/posts/[category]":[s,c,t,a,"static/chunks/pages/posts/[category]-731f2768a845326a.js"],"/[...detail]":[s,c,t,"static/chunks/393-7217afb82985f4a5.js",a,"static/chunks/pages/[...detail]-83887a767c3729b9.js"],sortedPages:["/","/_app","/_error","/posts/search","/posts/tag/[tag]","/posts/[category]","/[...detail]"]}}("static/css/570acc098c248c2f.css","static/css/0407c34f82b3c3db.css","static/chunks/135-4ea70e0295549ad8.js","static/chunks/979-7390dd4fc9e19803.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/_next/static/kbtYb12H_OTxS29UAs1xe/_ssgManifest.js b/_next/static/pwbOjWHyyP97zls4vVRix/_ssgManifest.js similarity index 100% rename from _next/static/kbtYb12H_OTxS29UAs1xe/_ssgManifest.js rename to _next/static/pwbOjWHyyP97zls4vVRix/_ssgManifest.js diff --git a/index.html b/index.html index 9d5143c..c8d18e9 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/Algorithm.html b/posts/Algorithm.html index 539b89b..4c0adca 100644 --- a/posts/Algorithm.html +++ b/posts/Algorithm.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git "a/posts/Algorithm/[PCCP \352\270\260\354\266\234\353\254\270\354\240\234]\355\215\274\354\246\220\352\262\214\354\236\204 \354\261\214\353\246\260\354\247\200.html" "b/posts/Algorithm/[PCCP \352\270\260\354\266\234\353\254\270\354\240\234]\355\215\274\354\246\220\352\262\214\354\236\204 \354\261\214\353\246\260\354\247\200.html" index 7ed3c17..fa630be 100644 --- "a/posts/Algorithm/[PCCP \352\270\260\354\266\234\353\254\270\354\240\234]\355\215\274\354\246\220\352\262\214\354\236\204 \354\261\214\353\246\260\354\247\200.html" +++ "b/posts/Algorithm/[PCCP \352\270\260\354\266\234\353\254\270\354\240\234]\355\215\274\354\246\220\352\262\214\354\236\204 \354\261\214\353\246\260\354\247\200.html" @@ -1 +1 @@ -[PCCP 기출문제] 퍼즐게임 챌린지
\ No newline at end of file +[PCCP 기출문제] 퍼즐게임 챌린지
\ No newline at end of file diff --git "a/posts/Algorithm/[\353\260\261\354\244\200 gold 5] \353\271\214\353\237\260 \355\230\270\354\204\235.html" "b/posts/Algorithm/[\353\260\261\354\244\200 gold 5] \353\271\214\353\237\260 \355\230\270\354\204\235.html" index 723dc88..dd680fc 100644 --- "a/posts/Algorithm/[\353\260\261\354\244\200 gold 5] \353\271\214\353\237\260 \355\230\270\354\204\235.html" +++ "b/posts/Algorithm/[\353\260\261\354\244\200 gold 5] \353\271\214\353\237\260 \355\230\270\354\204\235.html" @@ -1 +1 @@ -[백준 gold 5] 빌런 호석
\ No newline at end of file +[백준 gold 5] 빌런 호석
\ No newline at end of file diff --git "a/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\225\204\354\235\264\355\205\234 \354\244\215\352\270\260.html" "b/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\225\204\354\235\264\355\205\234 \354\244\215\352\270\260.html" index da506e1..d307274 100644 --- "a/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\225\204\354\235\264\355\205\234 \354\244\215\352\270\260.html" +++ "b/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\225\204\354\235\264\355\205\234 \354\244\215\352\270\260.html" @@ -1 +1 @@ -[프로그래머스 level 3] 아이템 줍기
\ No newline at end of file +[프로그래머스 level 3] 아이템 줍기
\ No newline at end of file diff --git "a/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\247\225\352\262\200\353\213\244\353\246\254\352\261\264\353\204\210\352\270\260.html" "b/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\247\225\352\262\200\353\213\244\353\246\254\352\261\264\353\204\210\352\270\260.html" index bee42c5..ed1b09d 100644 --- "a/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\247\225\352\262\200\353\213\244\353\246\254\352\261\264\353\204\210\352\270\260.html" +++ "b/posts/Algorithm/[\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244lv3]\354\247\225\352\262\200\353\213\244\353\246\254\352\261\264\353\204\210\352\270\260.html" @@ -1 +1 @@ -[프로그래머스 level 3] 징검다리 건너기
\ No newline at end of file +[프로그래머스 level 3] 징검다리 건너기
\ No newline at end of file diff --git a/posts/Javascript.html b/posts/Javascript.html index 5a24778..fb09909 100644 --- a/posts/Javascript.html +++ b/posts/Javascript.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/Javascript/javascriptdeepdive09.html b/posts/Javascript/javascriptdeepdive09.html index 703333d..2509e85 100644 --- a/posts/Javascript/javascriptdeepdive09.html +++ b/posts/Javascript/javascriptdeepdive09.html @@ -1 +1 @@ -자바스크립트 딥다이브: 09.타입 변환과 단축평가
\ No newline at end of file +자바스크립트 딥다이브: 09.타입 변환과 단축평가
\ No newline at end of file diff --git a/posts/Javascript/javascriptdeepdive10.html b/posts/Javascript/javascriptdeepdive10.html index 35fec62..14eff88 100644 --- a/posts/Javascript/javascriptdeepdive10.html +++ b/posts/Javascript/javascriptdeepdive10.html @@ -1 +1 @@ -객체 리터럴
\ No newline at end of file +객체 리터럴
\ No newline at end of file diff --git a/posts/Javascript/javascriptdeepdive11.html b/posts/Javascript/javascriptdeepdive11.html index 39ec812..fe71be3 100644 --- a/posts/Javascript/javascriptdeepdive11.html +++ b/posts/Javascript/javascriptdeepdive11.html @@ -1 +1 @@ -원시 값과 객체의 비교
\ No newline at end of file +원시 값과 객체의 비교
\ No newline at end of file diff --git "a/posts/Javascript/\355\201\264\353\241\234\354\240\200\354\227\220 \353\214\200\355\225\230\354\227\254.html" "b/posts/Javascript/\355\201\264\353\241\234\354\240\200\354\227\220 \353\214\200\355\225\230\354\227\254.html" index 2b34c4d..a0c779f 100644 --- "a/posts/Javascript/\355\201\264\353\241\234\354\240\200\354\227\220 \353\214\200\355\225\230\354\227\254.html" +++ "b/posts/Javascript/\355\201\264\353\241\234\354\240\200\354\227\220 \353\214\200\355\225\230\354\227\254.html" @@ -1 +1 @@ -클로저에 대하여
\ No newline at end of file +클로저에 대하여
\ No newline at end of file diff --git a/posts/React.html b/posts/React.html index 24194cf..22e59c6 100644 --- a/posts/React.html +++ b/posts/React.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git "a/posts/React/React useState \355\233\205\352\263\274 \355\201\264\353\241\234\354\240\200.html" "b/posts/React/React useState \355\233\205\352\263\274 \355\201\264\353\241\234\354\240\200.html" index fe6305e..6799d86 100644 --- "a/posts/React/React useState \355\233\205\352\263\274 \355\201\264\353\241\234\354\240\200.html" +++ "b/posts/React/React useState \355\233\205\352\263\274 \355\201\264\353\241\234\354\240\200.html" @@ -1 +1 @@ -React useState 훅과 클로저
\ No newline at end of file +React useState 훅과 클로저
\ No newline at end of file diff --git "a/posts/React/[React DeepDive] React\354\235\230 \352\260\200\354\203\201 DOM.html" "b/posts/React/[React DeepDive] React\354\235\230 \352\260\200\354\203\201 DOM.html" index c505f69..fc29768 100644 --- "a/posts/React/[React DeepDive] React\354\235\230 \352\260\200\354\203\201 DOM.html" +++ "b/posts/React/[React DeepDive] React\354\235\230 \352\260\200\354\203\201 DOM.html" @@ -1 +1 @@ -[React DeepDive] React의 가상 DOM
\ No newline at end of file +[React DeepDive] React의 가상 DOM
\ No newline at end of file diff --git a/posts/all.html b/posts/all.html index 10ee20f..5bac035 100644 --- a/posts/all.html +++ b/posts/all.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/search.html b/posts/search.html index 74e63fc..52f3624 100644 --- a/posts/search.html +++ b/posts/search.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/Algorithm.html b/posts/tag/Algorithm.html index 0c73a03..6068ba2 100644 --- a/posts/tag/Algorithm.html +++ b/posts/tag/Algorithm.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/JavaScript.html b/posts/tag/JavaScript.html index 0df8e66..5b1ee80 100644 --- a/posts/tag/JavaScript.html +++ b/posts/tag/JavaScript.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/PCCP.html b/posts/tag/PCCP.html index c56bcd6..bfd6638 100644 --- a/posts/tag/PCCP.html +++ b/posts/tag/PCCP.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/Virtual DOM.html b/posts/tag/Virtual DOM.html index 044d87f..aa46460 100644 --- a/posts/tag/Virtual DOM.html +++ b/posts/tag/Virtual DOM.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/all.html b/posts/tag/all.html index 239eda6..5365e93 100644 --- a/posts/tag/all.html +++ b/posts/tag/all.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/bfs.html b/posts/tag/bfs.html index a9567f2..2a19944 100644 --- a/posts/tag/bfs.html +++ b/posts/tag/bfs.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/deepdive.html b/posts/tag/deepdive.html index 20e8026..0d3a30a 100644 --- a/posts/tag/deepdive.html +++ b/posts/tag/deepdive.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/dfs.html b/posts/tag/dfs.html index 4fb2ab4..16bd60e 100644 --- a/posts/tag/dfs.html +++ b/posts/tag/dfs.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/gold.html b/posts/tag/gold.html index 105cbda..44f0749 100644 --- a/posts/tag/gold.html +++ b/posts/tag/gold.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/hook.html b/posts/tag/hook.html index f45f46c..a1346d8 100644 --- a/posts/tag/hook.html +++ b/posts/tag/hook.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/lv2.html b/posts/tag/lv2.html index 7fa577e..8d1f7a8 100644 --- a/posts/tag/lv2.html +++ b/posts/tag/lv2.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/lv3.html b/posts/tag/lv3.html index 90acdb0..352a339 100644 --- a/posts/tag/lv3.html +++ b/posts/tag/lv3.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/react.html b/posts/tag/react.html index 90802c8..cae805a 100644 --- a/posts/tag/react.html +++ b/posts/tag/react.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/posts/tag/useState.html b/posts/tag/useState.html index 68b5d78..308de2c 100644 --- a/posts/tag/useState.html +++ b/posts/tag/useState.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git "a/posts/tag/\353\260\261\354\244\200.html" "b/posts/tag/\353\260\261\354\244\200.html" index d604b0a..85952ce 100644 --- "a/posts/tag/\353\260\261\354\244\200.html" +++ "b/posts/tag/\353\260\261\354\244\200.html" @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git "a/posts/tag/\354\203\235\353\252\205\354\243\274\352\270\260.html" "b/posts/tag/\354\203\235\353\252\205\354\243\274\352\270\260.html" index 089b505..bfab2d0 100644 --- "a/posts/tag/\354\203\235\353\252\205\354\243\274\352\270\260.html" +++ "b/posts/tag/\354\203\235\353\252\205\354\243\274\352\270\260.html" @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git "a/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.html" "b/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.html" index 4bc2506..e2b1bfb 100644 --- "a/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.html" +++ "b/posts/tag/\354\235\264\353\266\204\355\203\220\354\203\211.html" @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git "a/posts/tag/\355\201\264\353\241\234\354\240\200.html" "b/posts/tag/\355\201\264\353\241\234\354\240\200.html" index f27f333..748e5aa 100644 --- "a/posts/tag/\355\201\264\353\241\234\354\240\200.html" +++ "b/posts/tag/\355\201\264\353\241\234\354\240\200.html" @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git "a/posts/tag/\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244.html" "b/posts/tag/\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244.html" index 6d85b4c..23841b5 100644 --- "a/posts/tag/\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244.html" +++ "b/posts/tag/\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244.html" @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file