728x90

 

Next 특정 버전 부터 app router 형식이 추가되었습니다.

전 주로 page router 형식만 다루었어서

공부차 app router에 대해 다루어 보려고 합니다.

기본적인 차이는 스택오버플로우 형님이 아래 표로 정리해주셨습니다.

참조 : https://stackoverflow.com/questions/76570208/what-is-different-between-app-router-and-pages-router-in-next-js 

 


[page.tsx]

 

기본적으로 이런 구조를 가진다. 

app 폴더에서 page.tsx가 처음으로 렌딩될 페이지 이다.

여기서 새로운 페이지를 추가하려면 폴더와 함께 새로운 page.tsx를 생성해준다.

그러고나면 /main 경로로 페이지가 추가가된다.

 

 

 

*여기서 프로출신들이 사용한다는 야무진 방법하나 추가*

(물론 프로출신이란 말은 죠큽니다.)

폴더 이름에 괄호'( )' 로 감쌀 수 있습니다. 이를 통해서 디폴트 그룹으로 묶을 수 있습니다!

 

위 처럼 (main)으로 폴더 명을 만들어두면 디폴트 페이지가 되고,

접근하려고 할때 /main 이 아닌 /  으로 바로 접속가능합니다!

물론 이때에는 기존에 /app에 있었던 page.tsx 파일을 삭제해주어야 한다는것~! 

있다면 (main)을 만들어도 루트에 있는 page.tsx를 바라봅니다.

 

( ) 괄호 스킬을 여러 폴더에 걸쳐서 사용할 수도 있는데,

어플리케이션의 커다란 분기를 나눌때도 사용할 수 있습니다.

 

위처럼 큰 분기를 (app), (web) 두개로 나누어서 개발을 하고 page.tsx에서

아래와 같이 분기를 쳐주면 모바일일 경우 (app) 폴더에서, 웹일 경우 (web) 폴더에서 

디폴트 경로가 설정되게 됩니다!

 

export default function MainPage() {
  return isMobile ? <MobileMain /> : <DesktopMain />;
}

 

[layout.tsx]

page.tsx 파일과 함께 있는 것이 layout.tsx 파일이다.

이는 여러 라우터들이 같이 공유하는 UI라고 생각하면 편하다.

이 layout 상에서는 상태가 보존되고 리렌더링이 발생하지 않는다.

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  );
}

 

기본적인 레이아웃의 코드인데, 해당 {children} 부분에 라우터가 들어온다고 생각하면 됩니다.

위에 만든 main 폴더안에도 layout을 새로 선언해줄 수 있습니다. 다만 최상단 /app 밑에는

반드시 layout.tsx파일이 있어야 합니다

 

또한 layout.tsx은 기본적으로 상위에서부터 중첩되어 적용된다.

main폴더에 layout을 만들었다고 해당 레이아웃만 적용되지 않고,

최상단에 존재하는 layout.tsx가 적용된 상태에서 추가적으로 적용된다.

아래는 NEXT에서 제공해주는 이해를 돕기위한 그림이다.

 

요롷고롬 dashboard 하위에 있는 layout은

최상단 layout속으로 들어갑니다.

 

 


 

 

보통 예전부터 있던 프로젝트에서만 개발을 진행하다보면 

 익숙하지 않은 새로운 구조가 나온 경우가 좀 있는것 같아요.

페이지 라우팅만 하다가 앱 라우터를 가볍게 알아보니 새롭군요..

이런게 개인의 뚝딱거림이 필요하다는 이유중 하나 아닐까 싶군요

 

 

참고 : https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts ㅁㄴ

 

 

 

728x90
728x90

 

특정페이지를 접속 시 로그인이 필요할 때가 있다.

로그인 완료 후 원하는 특정페이지로 바로 들어갈 수 있다면 

로그인 후에 단순 홈으로 갈때 보다 훨씬 좋은

사용자 경험을 제공할 수 있을 것이다.

 

해당 방식을 프런트에서 구현하려면 두 가지 방법이 있다.

 

1. 링크에 redirect 할 값을 추가해둔다.
 https://www.musinsa.com/auth/login?referer=https%3A%2F%2Fwww.musinsa.com%2Fmember%2Fmypage 
무신사 로그인 페이지 주소이다.

url뒤에 referer=....로 로그인 이후에 갈 페이지 정보를 표시해서 해당 값으로 이동하게 한다.

 

 

2. useNavigate()의 navigate 함수를 통해 props를 넘겨준다.

 

 

 

위 두 방법의 장단점은 확실하다.

첫번째는 url만으로 다 처리가 가능하고,

페이지 새로고침이 되어도 정보가 유지된다.

다만 정보가 url에 다 보인다는 점이 있다.

 

반면 두 번째는 여타 정보를  url에 노출하지 않기에 깔끔하지만,

페이지 새로고침시에 들고있던 정보가 사라지는 단점이 있다.

 

상황에 맞는 방법을 선택해서 진행하면 될듯하다.

여기선 두 번째 방법을 가볍게 체크해보려고 한다.

 

 


 

2. useNavigate()의 navigate 사용하기

 

router 쪽에서 로그인 체크후에 /login페이지로 강제 이동시킬때, 

navigate 함수에 pathname을 추가해서 보낸다.

const { pathname } = useLocation();

useEffect(() => {
    if (토큰이 없을때 && 로그인 페이지가 아닐때) {
      navigate("/login", { state: pathname });
    }
}, [pathname]);

 

 

그리고 로그인 페이지에서 state값을 불러와서

로그인 성공시에 해당 페이지로 이동하게끔 해주면 된다.

다만!!! 여기서 유저의 새로고침등의 행동으로 값이 없어질 수 있기 때문에 

기본값도 잊지말고 추가해주는 꼼꼼함이 필요하다!

const { state } = useLocation();

const redirectUrl = state || "/home";  // state가 없을때를 대비한 기본값 /home
navigate(redirectUrl);

 

 

728x90
728x90

여느날과도 같이 코딩중에

pathname을 가지고 놀기 위해서

useLocation 훅을 추가했다.

딱 코드에 쓰고 저장을 하는 순간 에러가 빡.

 

 

흐음..? 갑자기?

훅 위치가 잘못되었나.. 같은 페이지에 useState도 이미 사용중이라 아닌것 같은데..

어림도 없지. 바로 훅 위치의 잘못이었다 ^오^

BrowserRouterHashRouter 내부에서 useLocation 훅을  사용했어야 했다... 

 

 

 

해당 훅을 왜 내부에서 사용해야 할까?useLocation외에도  react-router-dom이 제공해주는 훅들은 모두 BrowserRouter HashRouter 내부에서 사용되어야 한다.

 

 

 

위 훅들은 현재 위치, 탐색, 라우터 상태등을 제공하는 라우터 컨텍스트에 의존하는데,

이 컨텍스트는 BrowserRouter나 HashRouter에서 생성하고

어플리케이션의 하위 컴포넌트로 전달된다. 

라우터 컨텍스트가 없는 밖에서 useLocation 훅을 사용하고자 했으니참고할 컨텍스트가 없어서 에러가 발생했던것!

 

 

 

 

 

728x90
728x90

배열에서 중복인 친구들을 제거 해주는 일은 문제를 풀거나 

작업할 때 심심찮게 등장한다.

 

const array = [1, 2, 3, 3, 4, 5, 5, 5, 6]

// filter
1. array.filter((v,i) => array.indexOf(v) === i); 
// reduce
2. array.reduce((acc, cur) => acc.includes(cur) ? acc : [...acc, cur], []);
// Set
3. [...new Set(array)]

 

filterreduce 둘다 해당 아이템의 index와

아이템이 처음으로 등장하는 indexOf 와의 일치 여부를 통해서

해당 값이 중복인지 아닌지를 판단한다. 스무스~

 

근데 Set... 은 매우 간단한 방식으로 해결해준다. 

 

간간히 Set과 마주치기는 했지만.. 배열이 손에 이미 익어있다보니..

손이 가지 않는다. 일단 나부터 개추

하지만 나오는 문법에는 이유가 있을 터, 

또 위처럼 간단하게 처리할 수도 있으니.. 간단히 체크해보자!

 


Set

일단 배열은 순서가 있다. 우리가 index를 통해서 접근할 수 있으니 말이다.

또 위에 예시처럼 같은 값을 여러개 저장할 수도 있다. 

 

하지만 Set는 순서와 중복이 없는 데이터의 집합이다.

데이터를 순서 없이 저장하고 중복된 값이 있으면 값을 저장하지 않는다.

(후에 다루겠지만 그래서 원하는 데이터를 뽑을 수가 없다 --) 

 

Set 사용해보기

 

개발자 창을 이용해 선언하면 아래와 같이 나온다.

기본 값을 넣으면 아래와 같다.

 

기본적인 함수들은 아래와 같다. 매우 직관적인 영어를 사용하고 있어서 기억하기 좋다!

set.add('item')    // item  추가 
set.delete('item') // item  삭제, 해당 요소가 있으면 true, 없으면 false 리턴'
set.has('item')    // item  존재 여부
set.size           // set의 값 갯수
set.clear()        // set값 전체 제거

 

 

Set을 통해서 활용하는 꿀팁은 아래정도 있지 싶다.

// 중복값 쉽게 제거하기
const array = [...new Set(ARRAY)]  

// 집합
const setA = new Set(['A', 'B']);
const setB = new Set(['B', 'C']);

const 합집합 = new Set([...setA, ...setB]);
const 교집합 = new Set([...setA].filter(value => setB.has(value)))    
const 차집합 = new Set([...setA].filter(value => !setB.has(value)))

// 순회
for (const v of set){...}

 

 

그럼 Object를 요소로 가지는건?

object를 요소로 가질 수도 있다.

set.add({id:1, value: 'a'}) 

방식으로 넣을 수 있다. 물론 중복값 체크에 문제가 있을 수 있다.

객체의 참조를 비교하기 때문에

중복된 값이 추가되는 것 처럼 보일 수도 있다..!

흠... 별로 손이 안가겠는데?

 

Set.. 왜 별로 안보일까?

아까 말했듯 순서가 따로 존재하지 않는 구조이기 때문에

원하는 요소를 찾기 위해서는 순회를 하면서 요소를 찾아내야한다.

예에..?

물론 손에 안익어서 안쓰이는 부분도 있지만

아마 이런 부분 때문에 널리 쓰이지 않는것 같다. (뇌피셜)

보통 받아온 데이터 중 원하는 이런저런걸 쓰고~ 하는데

원하는 값을 전체 데이터를 순회하면서 찾아야만 한다면.. 

 


 

그럼 Set은 중복 제거하는 알고리즘 문제 풀라고 나온건가요.? 
 

 

728x90
728x90

nextjs에서 props 크기를 체크해 볼 수 있을까?!

한줄의 코드로 확인 가능하다 :)

원하는 페이지로 이동한 후 아래 명령어를 실행한다.

 

document.getElementById("__NEXT_DATA__").text

// 깔끔하게 보고싶다면
JSON.parse(document.getElementById("__NEXT_DATA__").textContent)

 

그러면 {"props":,,,,,} 하면서 쭈욱 나온다. 그리고

용량이 체크된다. 

 

특정페이지에서 Warning: data for page .... exceeds the threshold of 128 kB, this amount of data can reduce performance. 라는 워닝이 서버쪽에서 나와서 확인하다가 발견한 내용이다.

 

용량과 props의 세부 내용을 확인해서 필요한 부분들만 페이지로 넘겨주면서 용량을 줄여야 좋은 사용자 경험을 주는 페이지로 변신 시켜보자!

 

어서 작업하러....이만,..

728x90
728x90
const conditionRender = (item: string) =>{
	switch(item){
    	case: 'A'
    	...
        return ..
    }
}

<Page>
  {conditionRender(item)}   // 이렇게 구현
</Page>

 

 

개인적으로 코드 작업시에 조건부로 렌더링해야할 때 위와 같은 방식을 사용하고는 했다. 조건에 따라서 보여줘야 하는 컴포넌트를 선택하는 컴포넌트 함수를 만들고 렌더링해야하는 부분에 { } 를 사용하여 해당 함수를 넣어주는 식으로 작업했다. 이렇게 되면 편히 상태에 따라 원하는 컴포넌트를 리턴해 줄 수 있었다. 

 

물론 이런 방법이  엄청난 에러가 발생하지는 않는다. 그랬다면 내 프로그램에 이미 난리가 났을 것이다. 하지만 이런 방식은 리엑트에서 권장하는 방법이 아니므로 수정해줘야 한다. React에서는 위와 같은 component function을 직접적으로 부르는 것을 금하고 있다. 공식 문서에서는 그냥 네버!!! 라고 강조하고 있다. 공식문서 링크는 아래 참고에 달려있다. 한번씩 읽어보길 권장한다. 해당 공식문서에서 말하는 부분은 이러하다. 

 

컴포넌트는 JSX만 사용하라고 권장한다. 이를 통해서 렌더링시에 React가 더 잘 관리하게 끔 할 수 있기 때문이다.

어떤 컴포넌트가 다시 렌더링이 필요한지 등의 결정이 쉽기에 재조정 과정이 훨씬 효율적이게 되는 것이다.  이전 글이었던 내용과 관련된 부분인것 같다. 해당 계산 과정을 온전히 React에 넘겨서 렌더링 계산을 효율적이게 만들려고 하는게 아닌가 싶다. (이전 글: https://choq.tistory.com/60)

 

그렇다면 코드는 어떻게 할까? 어렵지 않다. 해당 부분을 컴포넌트로만 수정해주면 된다.

const ConditionRender = (item: string) =>{
	switch(item){
    	case: 'A'
    	...
        return ..
    }
}

<Page>
  <ConditionRender item ={...} />   // 이렇게 수정
</Page>

 

 

 

 

 

참고 : https://react.dev/reference/rules/react-calls-components-and-hooks#never-call-component-functions-directly
728x90

'코뷰 리뷰' 카테고리의 다른 글

[코드리뷰] 순차적 함수 한번에 풀어보기  (2) 2024.05.31
728x90

https://choq.tistory.com/59  : 근데 왜 index를 key 값으로 쓰면 안좋다는 거에요?

 

이전 글의 상위 개념에 대한 부분을 다루고자 한다.

 


 

React의 Reconciliation에 대한 이야기다.

 

 

영어 뜻은 '화해' 이다. 갑자기 화해? 라고 생각할 수 있지만....

[원래 의미는 불화 후 화해하는 것을 의미한다.  여기서 나아가 '하나의 마음으로 일관성 있게 만든다' 라는 의미로 진화했단다-https://www.etymonline.com/kr/word/reconciliation

....고로 다름을 찾아서 일치시킨다, 정도로 해석하면 될듯 하다. 

 

 

그래서  React에서의 Reconciliation이란,

이전과 비교해서 다른 점을 찾아내고, 일치하게 만들어 재렌더링하는 일련의 과정을 의미한다.

 

자 그럼 무슨 비교를 통해서 현상태와 다른 점을 찾아낼까? React에서는 VirtualDOM을 사용하니까 DOM과의 비교를 통해서 하겠지~? 

라고 VirtualDOM을 떠올렸다면 나름 멋쟁이. 하지만 비교 대상은 DOM이 아니다. WHAT?? 바로 또 다른 VDOM(VirtualDOM)이다.

 

VDOM은 다음의 두 상태로 존재 한다. Current 트리WorkinProgress 트리이다. 

전자는 DOM에 그려진것과 동일한 트리, 후자는 특정 이벤트에 의해 변화or렌더링 되고있는 트리를 의미한다. 이를  더블 버퍼링 구조 라고 한다. 더블 버퍼링은 A버퍼에서 연산이 일어나면 B 버퍼가 값을 보여주고, A버퍼에서 연산이 끝나면 A버퍼를 보여주면서 B버퍼를 그 다음 연산에 투입시키는 방식으로 작동한다.  WorkinProgress 트리에서 연산이 끝나고 트리가 그려지면 DOM에 적용되면서 이제 Current 트리가 된다. (신분상승!?) 그러면서 기존에 Current 트리였던 버퍼는 WorkinProgress 트리가 되어 다음 연산을 진행한다. 

이런 과정을 통해서 화면 깜빡임이 줄어들면서 부드럽게 표현된다. 다만 화면을 두개 그리기에 구조가 복잡하고 메모리 사용량이 증가하는 부분이 있다고는.. 하지만 이런 부분은 리엑트가 해줄것이다ㅎ우린 맘편히 코드만 짜면된다.

더블 버퍼링의 구조도, Buffer2로 IN시에 Buffer1이 유저에게 보여준다.

 

추가적으로 트리끼리 차이를 비교하는 방식은 비교 알고리즘(Diffing Algorithm)을 사용한다.

비교하는 트리의 루트부터 비교해가는 방식을 말한다.

엘리먼트가 다르다면 아예 새로운 트리를 그리고, 같다면 그 안에서 변경된 부분만 업데이트 해준다. 

이 비교하고 업데이트 해줄때 key값의 중요성이 빛을 발하는 것이고, 그러한 장점을 사용하기 위해 index사용을 지양해야하는 것이다.

 

 

 

 

 

[참고]

1. https://medium.com/@cksal4785911/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EA%B9%8C%EB%B3%B4%EC%9E%90-fbcc5b9c9e00

2. https://velog.io/@boyeon_jeong/VDOM     

 

 

728x90
728x90

 

 

참조 : https://yozm.wishket.com/magazine/detail/2634/

 

 

너무 야무진 글을 읽고나서 남겨두기 위해서 조금 정리해본다.

 

map 등의 반복적인 함수에서 컴포넌트 사용시 key값을 반드시 사용해줘야 한다. 여기서 key값은 index값으로 사용하지 말라고 보통 권장한다. 이때 보통 이유를 배열이 변경이 되었을 때 성능저하가 있을 수 있다고 설명한다. 하지만 저 블로그는 여기서 한 발 더 나아가 왜 성능저하가 오는가에 대해 디테일하게 들어간다. 역시 프로출신들의 자세는 다르다. 그럼 우리도 한번 파고 들어보자.

 

key는 무슨 역할을 하나요?

<div>
  <li>밤편지</li>
  <li>너랑나</li>
</div>

 

위와 같은 리스트가 있다고 보자. [밤편지][너랑나] 뒤에 새로운 요소가 추가 된다면 해당 요소만 계산하면 된다.

하지만 만약 새로운 요소가 [밤편지][너랑나]의 앞에 추가 된다면?

[밤편지], [너랑나] 그리고 추가 요소까지 모두 새롭게 계산을 해줘야한다.

지금은 요소가 적어서 계산량도 적지만

리스트가 점점 많아질 수록 재계산을 많이 하게 된다. 여기서 key값이 중요한 역할을 한다.

 

<div>
  <li key='nightLetter'>밤편지</li>
  <li key='uandme'>너랑나</li>
</div>

위 구조에서 key값만 추가한 상태이다. 여기서 새로운 요소가 앞이나 뒤에 추가되면 어떻게 될까?

key값이 바뀌지 않는 이상, [밤편지]와 [너랑나]는 새로 계산되지 않고 새로 추가될 요소만 계산된다.

이게 key의 역할이다. key값이 변하지 않는다면 계산을 다시 진행하지 않고 이미 가지고 있는 값을 사용한다.

이런식으로 불필요한 계산을 줄이면서 성능 최적화를 이끈다. 

 

key값을 index로 사용한다면?

key를 사용하는 의미가 없어지게 된다. 위에 말 대로 key값의 변화를 가지고 재계산의 유무를 산정하는데,

index를 key로 사용한다면 매번 key값이 달라지는 결과를 가지고오고 모든 리스트 값이 재계산되게 한다. (안하니만 못하다는 말이..) 

 

또한 즉석에서 key값을 만들어서 사용하는 경우도 마찬가지다. 오히려 index를 사용할 때 보다도 더 많은 계산이 필요하다.

index는 적어도 추가, 삭제된 위치의 다음 리스트부터 모두 재계산되지만

즉석에서 만든 값을 추가한다면 모든 이벤트때 마다 리스트를 계속해서 계산해줘야하는 불상사를 가져온다. 

그러니 고민없이 그냥 item 의 id 값을 key값으로 사용합시다.

 


 

 

참조한 글 마지막의 말처럼,

key에 index를 사용하면 안된다고만 알고있는 사람과

이는 성능저하를 일으킬 수 있다는 사실을 아는 사람은 다르다는 것에 동의합니다.

또한 이런 깨달음을 통해서

다른 부분에 있어서도 새로운 관점을 가지고 이유와 문제를 파고드는 시각도 생길 수 있다고 봅니다.

열코딩합시당 :)

 

728x90
728x90

 

A... import should occur before import of .....B

 

eslint-plugin-import설치 후 나오는

린트 에러이기 때문에 구동자체가 문제가 되지는 않는다.

하지만 이런 린트 규칙은 이유가 없지는 않을터,

하지만 import 구문은 하나의 코드 파일의 서두와도 같다.

깔끔하게 정렬된 import문은 대략의 코드를 한눈에 보게끔 할 수 있을 것이다.

 

일반적인 import 순서는 아래와 같다.

// Standard Library Imports
import path from 'path';
import fs from 'fs';

일반적인 라이브러리를 최상단에 import해준다.

 

// Third-Party Imports
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import lodash from 'lodash';

그리고 리엑트나, 서드파티 라이브러리를 import해준다.

 

// Absolute Imports (internal)
import utils from 'utils';
import config from 'config';

// Relative Imports (internal)
import MyComponent from './components/MyComponent';
import { someFunction } from './utils/helpers';

그리고 나서 절대경로, 상대경로의 순으로 import 해준다.

 

 

정리하자면, 

1. 일반적인 라이브러리

2. 서드파티

3. 절대경로

4. 상대경로

순으로 import 해주면된다.

 

import 순서

가 코딩 실력을 다이내믹하게 바꿔주지는 않겠지만

이런 세세한 디테일이 쌓여서 

프로가 되는게 아닐까!

728x90
728x90

-맥 터미널 기준으로 작성되었습니다.-

 

터미널에서 PATH나 여타 설정을 할 필요가 있을 때,

zshrcbashrc 등의 파일을 사용한다.

또, 도커나 여타 설정 파일들을 터미널에서 간단히 확인하고자 할때 

우리는 보통 cat ${원하는 파일}, vim ${원하는 파일}  등의 명령어를 사용해서 파일을 체크한다.

물론 사용할때마다 눈이 아프다...

하얗게만 꾸며져 있는 이 터미널...

.... 그저 검은 땅에 내린 눈 ...

 

이런 삭막한 터미널은 그만.

바~로 프로 출신들의 비기를 사용할 시간.

bat 명령어 하나만 있으면 해결할 수 있다.

일단 기본적으로는 내장되어 있지는 않아서 다운로드 해주어야 한다.

$ brew install bat

// m1등에서 Rosetta2 관련 에러가 발생하면 아래 명령어로 실행 
$ arch -arm64 brew install bar

 

 

그러고 나서는 다른 것과 같이 써주면 된다.

   bat ${명령어}   

 

???

내가 아까 vim 과 cat으로 보던 그 파일 맞나 싶을꺼다.

알록달록 전혀 다른 비주얼이 기다리고 있다.

 

라인 넘버와 export 등의 색 표시까지....

진짜 깔끔하다. 그저 GOAT...

 

보기 좋게 코딩하쟈 레쯔고우.

 

 

728x90

+ Recent posts