Next13 미들웨어 사용법.
프로젝트 최상단 루트에
middleware.js(tsx) 파일을 만들어서 작성하면
자동으로 미들웨어로서 기능으로 서버작업에 참견한다.
공부코드
import { getToken } from 'next-auth/jwt';
import { NextResponse } from 'next/server';
const middleware = async (request) => {
const session = await getToken({ req: request }) // 로그인 정보 확인.
if (request.nextUrl.pathname.startsWith('/write')) { // write 사이트 접속시 검사
if (session === null) { // 로그인안되면 다른url로 보내버린다.
return NextResponse.redirect(new URL('http://localhost:3000/list'), request.url)
}
return NextResponse.next()
}
// 현재 요청중인 url //리스트 페이지에 접속하면 실행 스타츠위츠는 list 시작하는 모든 페이지 검사
if (request.nextUrl.pathname.startsWith('/list')) {
console.log(new Date()) // 시간 알려줌
console.log(request.headers.get('sec-ch-ua-platform'))//사용자 시스템 확인
return NextResponse.next() // 리턴하고 꼭 써야함 별일없으니 통과시켜주세요 뜻
}
if (request.nextUrl.pathname.startsWith('/register')) {
if (request.cookies.has('visited') === false) {
const response = NextResponse.next()
response.cookies.set({
name: 'visited',
value: 'true',
maxAge: 3600,
httpOnly: true
})
return response //쿠키생성
}
return NextResponse.next()
}
}
export default middleware;
쿠키를 다루는 문법.
import { NextResponse } from 'next/server';
export async function middleware(request) {
request.cookies.get('쿠키이름') //출력
request.cookies.has('쿠키이름') //존재확인
request.cookies.delete('쿠키이름') //삭제
const response = NextResponse.next()
response.cookies.set({
name: 'mode',
value: 'dark',
maxAge: 3600,
httpOnly : true //자바스크립트로 쿠키를 조작하는것을 방지
})
return response //쿠키생성
}
서버는 요청이 들어오면 응답해주는 간단한 프로그램입니다.
- DB 조회해보기
- 로그인 안한 사람이 /어쩌구 하위 경로로 GET, POST요청 하는걸 막기
- 이상한 페이지 접속하면 다른 곳으로 이동시키기
- 쿠키만들어주기
그리고 서버는 이런 여러가지 짓거리가 가능합니다.
그런데 간혹 하나의 서버기능을 100만개의 서버 API에 동시에 적용하고 싶은 경우가 있습니다.
그럴 땐 middleware 쓰면 가능합니다.
요청이랑 응답 사이에 간섭하는 코드라서 middleware 라고 부릅니다.
middleware.js
(/middleware.js)
import { NextResponse } from 'next/server'
export async function middleware(request) {
(실행할코드~~)
}
Next.js에선 app폴더와 나란한 위치에
middleware.js 파일 만들고 함수 하나 만들면 middleware 작성이 가능합니다.
그럼 여러분이
(1) 서버로 GET POST 등 요청을 날리거나
(2) 페이지 접속할 때마다 위 코드가 실행됩니다.
정확한 시점은 서버 API 실행이나 페이지 로드가 되기 전에 위 코드가 실행됩니다.
그래서 여러분 맘대로 요청을 가로채고 도청하고 간섭하고 할 수 있는 것임
(/middleware.js)
import { NextResponse } from 'next/server'
export async function middleware(request) {
console.log(request.nextUrl) //유저가 요청중인 URL 출력해줌
console.log(request.cookies) //유저가 보낸 쿠키 출력해줌
console.log(request.headers) //유저의 headers 정보 출력해줌
NextResponse.next() //통과
NextResponse.redirect() //다른페이지 이동
NextResponse.rewrite() //다른페이지 이동
}
middleware 안에선 이런 여러가지 것들을 사용할 수 있습니다.
진짠지 궁금하면 한두개 실행해보십시오.
- headers 에는 유저가 쓰는 브라우저, 언어, OS 정보 등이 들어있습니다.
- headers, cookies 하나 출력은 .get('항목이름') 뒤에 붙입시다. Map 자료형이라 그렇습니다.
- NextResponse 를 잘 쓰면 유저를 별일 없이 통과시켜주거나 다른 곳으로 이동시킬 수 있습니다.
- rewrite()는 다른 페이지 이동인데 브라우저 주소창에 뜨는 URL을 변경하지 않고 이동해줌
1. 특정 페이지 접속하는 놈들 기록하기
어떤 놈이 /list 페이지 들어올 때 마다
터미널에 유저정보, 현재시간 이런걸 출력해서 몰래 감청을 해보도록 합시다.
request라는 변수 출력해서 유심히 보면 유저 ip, geo 등 여러 정보들이 들어있는데 실제 사이트 배포시 사용가능하고
지금은 현재시간과 유저의 접속기기 OS 정도만 터미널에 출력해봅시다.
(/middleware.js)
import { NextResponse } from 'next/server'
export async function middleware(request) {
if (request.nextUrl.pathname == '/list') {
console.log(new Date().toLocaleString())
console.log(request.headers.get('sec-ch-ua-platform'))
return NextResponse.next()
}
}
request.nextUrl.pathname 출력하면 현재 요청하는 URL이 나오는데
그게 /list와 일치하면 저런 내용 출력해보라고 코드짰을 뿐입니다.
sec-ch-ua-platform 출력하면 OS정보가 나오는지 어떻게 알았냐고요?
구글찾아보거나 request.headers 출력해보면 알 수 있습니다.
(다만 사파리 브라우저로 접속시 사용불가)
if (request.nextUrl.pathname.startsWith('/list') ) { }
참고로 URL 뒤에 query string이라든지 그런걸 유저 맘대로 붙일 수 있기 때문에
URL을 검사할 땐 등호보다는 .startsWith() 사용하는게 좋습니다.
그럼 '/list' 라는 글자로 시작하는 URL을 전부 잡아낼 수 있습니다.
'/list/어쩌구' 같은 하위경로도 잡아낼듯
아무튼 그래서 이런 정보들을 DB에 보관하거나 날짜별 파일로 기록하는 사이트들도 있습니다.
2. 로그인 안된 유저는 /write 페이지 접속 못하게 막자
Next-auth를 이용해서 로그인기능을 구현해놨는데
이걸 이용해서 middleware 에서도 로그인 여부를 판단할 수 있습니다.
(.env 파일)
NEXTAUTH_SECRET=qwer1234
일단 이거 하려면 .env파일에 NEXTAUTH_SECRET 아무거나 집어넣는 사전작업이 필요합니다.
[...nextauth].js 파일에 넣던 secret : 'qwer1234' 이런거 그대로 똑같이 사용하면 됩니다.
JWT 암호화해주는 키라 길게 할 수록 좋습니다.
import { NextResponse } from 'next/server';
import { getToken } from "next-auth/jwt";
export async function middleware(request) {
if (request.nextUrl.pathname.startsWith('/write')) {
const session = await getToken({ req : request })
console.log('세션', session)
if (session == null) {
return NextResponse.redirect(new URL('/api/auth/signin', request.url));
}
}
}
(1) await getToken() 쓰면 현재 로그인한 유저의 정보를 출력할 수 있습니다.
(2) JWT를 사용중일 때만 사용가능할텐데
session 사용시엔 session 쿠키가 있는지,
그리고 그 session이 DB에 있는지 조회해보고 이상이 없는지 확인하는 코드로 비슷하게 구현가능합니다.
(3) redirect/rewrite 함수 안에는 그냥 저 자리에 이동할 URL만 잘 채우면 유저를 강제로 이동시킵니다.
NextResponse.redirect('http://localhost:3000/api/auth/signin');
▲ 이동시 뭔가 안되면 직접 절대경로로 작성하면 됩니다.
Q. /write 페이지 컴포넌트 안에서 if문 써도 똑같이 구현할 수 있는거 아님?
- /write 말고 다른 막고싶은 페이지들이 더 있으면 여기 쓰는게 편할 수도 있습니다.
3. 특정페이지 접속시 쿠키를 만들어보자
다크모드 버튼만들 때 쿠키생성을 프론트엔드에서 했었는데 (useEffect)
그게 싫으면 서버에서 만들어줘도 됩니다.
서버 API에서 만들어도 되고 middleware에서 만들어도 되는데 우리는 middleware를 써봅시다.
import { NextResponse } from 'next/server';
export async function middleware(request) {
request.cookies.get('쿠키이름') //출력
request.cookies.has('쿠키이름') //존재확인
request.cookies.delete('쿠키이름') //삭제
const response = NextResponse.next()
response.cookies.set({
name: 'mode',
value: 'dark',
maxAge: 3600,
httpOnly : true
})
return response //쿠키생성
}
여기선 이런 문법으로 쿠키들을 다룰 수 있습니다.
브라우저에 있는 모든건 언제나 유저가 조작이 가능하기 때문에
조작하면 큰일나는 정보는 서버에 보관하도록 합시다.
참고로 httpOnly를 true로 설정한 쿠키는 유저가 자바스크립트로 맘대로 조작이 불가능하게 막을 수 있습니다.
그래서 약간 더 안전할 수 있는데 크롬 개발자도구에서 직접 수정하는건 막을 수 없음
Q. 유저가 /register 페이지 방문시 visited=true 라는 쿠키를 생성해주려면 코드를 어떻게 짜야할까요?
같은 이름의 쿠키가 이미 있으면 아무 짓거리도 안해줘야합니다.
import { NextResponse } from 'next/server';
export async function middleware(request) {
if (request.nextUrl.pathname.startsWith('/register')) {
if (request.cookies.has('visited') == false) {
const response = NextResponse.next()
response.cookies.set({
name: 'visited',
value: 'true',
maxAge: 3600,
})
return response
}
return NextResponse.next()
}
}
return을 만나면 return 들어있던 함수실행이 조기종료되고 그 뒤에 있는 코드들을 실행하지 않습니다.