리팩토링 중에 폴더 구조, 모듈 이름 등 다양한 변경이 발생했습니다.
이런 변경 후에는 해당 파일이 참조된 위치나 이름을 수정해 줘야하는데, 미루고 미뤘죠..
그 결과, 식은땀을 흘리며 코드 수정을 진행해야 했습니다.
다행스럽게도 잘못된 참조를 수정하고 빠르게 수정이 불가능한 부분은 주석처리하여 정상적으로 배포가 되었습니다.
사진 상에서는 몇 개 안 보이지만 아래에 10개가 넘는 Error난 배포들이 있습니다.
배포 오류 중 골치아팠던 오류는 NextAuth 관련 오류였습니다.
다른 오류들은 단순 참조 오류였기 때문에 임시적인 주석처리와 수정으로 해결할 수 있었습니다만, NextAuth 오류는 뭐가 문제인지 도통 모르겠더군요.
오류의 내용은 다음과 같았습니다.
src/app/api/auth/[...nextauth]/route.ts
Type error: Route "src/app/api/auth/[...nextauth]/route.ts" does not match the required types of a Next.js Route.
"handler" is not a valid Route export field.
import NextAuth, { AuthOptions } from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { FirebaseError } from 'firebase/app';
import { FirebaseAuthError } from '@/error/firebaseAuthError';
import signInFirebase from '@/_utils/signInFireBase';
const handler: AuthOptions = NextAuth({
jwt: {
secret: process.env.NEXTAUTH_SECRET,
},
providers: [
CredentialsProvider({
name: 'showfinnmore',
credentials: {
email: { label: 'email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
if (!credentials || !credentials.email || !credentials.password)
return null;
try {
const user = await signInFirebase(
credentials.email,
credentials.password
);
if (user) {
return {
id: user.id,
name: user.name,
email: user.email,
};
}
return null;
} catch (error) {
if (error instanceof Error) {
const firebaseError = error as FirebaseError;
const errorCode = firebaseError.code;
const message = firebaseError.message;
throw new FirebaseAuthError(errorCode, message);
} else {
throw new Error('알 수 없는 에러가 발생했습니다.');
}
}
},
}),
],
pages: {
signIn: '/account/login',
},
callbacks: {
async redirect({ url, baseUrl }) {
return baseUrl;
},
},
});
export { handler as GET, handler as POST };
폴더 이름이 문제일까, 파일 이름이 문제일까, 여러 번의 삽질 후 빛과 같은 글을 찾아냈습니다.
Why does my NextAuthJS discord login work in the test environment but cannot be built to production? (NextJS 13)
여기에서 가장 반응이 많은 댓글을 보면 다음의 코드와 같이 authOptions
를 NextAuth
로 넘긴 후 반환 받은 값을 handler
로 할당해서 export
하면 해결이 된다고 합니다.
import NextAuth, { NextAuthOptions } from "next-auth";
import DiscordProvider from "next-auth/providers/discord";
const authOptions:NextAuthOptions = {
providers: [
DiscordProvider({
clientId: process.env.DISCORD_CLIENT_ID!,
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
})
]
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
제 코드를 다음과 같이 수정했습니다.
import NextAuth, { AuthOptions, NextAuthOptions } from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { FirebaseError } from 'firebase/app';
import { FirebaseAuthError } from '@/error/firebaseAuthError';
import signInFirebase from '@/utils/signInFirebase';
const authOptions: NextAuthOptions = NextAuth({
jwt: {
secret: process.env.NEXTAUTH_SECRET,
},
providers: [
CredentialsProvider({
name: 'showfinnmore',
credentials: {
email: { label: 'email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
if (!credentials || !credentials.email || !credentials.password)
return null;
try {
const user = await signInFirebase(
credentials.email,
credentials.password
);
if (user) {
return {
id: user.id,
name: user.name,
email: user.email,
};
}
return null;
} catch (error) {
if (error instanceof Error) {
const firebaseError = error as FirebaseError;
const errorCode = firebaseError.code;
const message = firebaseError.message;
throw new FirebaseAuthError(errorCode, message);
} else {
throw new Error('알 수 없는 에러가 발생했습니다.');
}
}
},
}),
],
pages: {
signIn: '/account/login',
},
callbacks: {
async redirect({ url, baseUrl }) {
return baseUrl;
},
},
});
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Vercel에서 배포 시 오류가 표시되지는 않았지만 콘솔에서 TypeError: options.providers is not iterable
오류가 발생했습니다.
생각해보면 NextAuth
로 받은 데이터(authOptions
)를 다시 한 번 NextAuth
로 보내 반환받는 게 이상했습니다. 다시 위의 코드를 보니 제가 잘못 작성한 거 였네요.
다음과 같이 handler
를 삭제하고 authOptions
을 내보내니 TypeError: options.providers is not iterable
오류는 해결되었는데요..
import NextAuth, { AuthOptions, NextAuthOptions } from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { FirebaseError } from 'firebase/app';
import { FirebaseAuthError } from '@/error/firebaseAuthError';
import signInFirebase from '@/utils/signInFirebase';
const authOptions: NextAuthOptions = NextAuth({
jwt: {
secret: process.env.NEXTAUTH_SECRET,
},
providers: [
CredentialsProvider({
name: 'showfinnmore',
credentials: {
email: { label: 'email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
if (!credentials || !credentials.email || !credentials.password)
return null;
try {
const user = await signInFirebase(
credentials.email,
credentials.password
);
if (user) {
return {
id: user.id,
name: user.name,
email: user.email,
};
}
return null;
} catch (error) {
if (error instanceof Error) {
const firebaseError = error as FirebaseError;
const errorCode = firebaseError.code;
const message = firebaseError.message;
throw new FirebaseAuthError(errorCode, message);
} else {
throw new Error('알 수 없는 에러가 발생했습니다.');
}
}
},
}),
],
pages: {
signIn: '/account/login',
},
callbacks: {
async redirect({ url, baseUrl }) {
return baseUrl;
},
},
});
export { authOptions as GET, handler as POST };
다시 Type error: Route "src/app/api/auth/[...nextauth]/route.ts" does not match the required types of a Next.js Route. Invalid configuration "GET":
오류가 발생했습니다.
Auth 관련 옵션을 authOptions
을 객체에 담아 NextAuth
로 넘겨 받은 데이터를 handler
에 저장하니 해결되었습니다.
어째서 객체 리터럴은 안 되고 변수를 통해 전달하는 것은 되는지는 정보가 없네요 ...
어쨋든 아래 글에서 나온 코드대로만 하면 잘 됩니다 ! 괜히 이것도 되겠지 하다가,,,,ㅠ
(Why does my NextAuthJS discord login work in the test environment but cannot be built to production? (NextJS 13))
import NextAuth, { NextAuthOptions } from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { FirebaseError } from 'firebase/app';
import { FirebaseAuthError } from '@/error/firebaseAuthError';
import signInFirebase from '@/_utils/signInFirebase';
const authOptions: NextAuthOptions = {
jwt: {
secret: process.env.NEXTAUTH_SECRET,
},
providers: [
CredentialsProvider({
name: 'showfinnmore',
credentials: {
email: { label: 'email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
if (!credentials || !credentials.email || !credentials.password)
return null;
try {
const user = await signInFirebase(
credentials.email,
credentials.password
);
if (user) {
return {
id: user.id,
name: user.name,
email: user.email,
};
}
return null;
} catch (error) {
if (error instanceof Error) {
const firebaseError = error as FirebaseError;
const errorCode = firebaseError.code;
const message = firebaseError.message;
throw new FirebaseAuthError(errorCode, message);
} else {
throw new Error('알 수 없는 에러가 발생했습니다.');
}
}
},
}),
],
pages: {
signIn: '/account/login',
},
callbacks: {
async redirect({ url, baseUrl }) {
return baseUrl;
},
},
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Vercel에서 배포 오류가 나타나도 리팩토링 끝나고 해야지 하면서 미뤘습니다만, 앞으로 바로바로 해결해야겠습니다.
이름이나 위치 변경하면 바로바로 import된 곳에서 반영을 해 주는 것도 중요한 것 같습니다.
리팩토링 시작할 때 branch를 나눠서 해당 branch에 push를 했어야 했는데, 그 부분을 놓쳐 일이 조금 복잡해 진 거 같습니다..
임시적으로 주석처리한 부분을 다시 살려낸 후에 branch를 나눌 예정입니다.