
FE(Next.js)에서 await ApiService.call()로 BE(Spring Boot) 호출 중 POST http://localhost:3000/api/proxy 500 (Internal Server Error) 에러와 마주쳤다.
호출 구조는
FE → localhost:3000/api/proxy → localhost:9300/api/transaction → BE TransactionController
순서였다.
로컬 테스트 중이고 서버 측 에러라고 생각해
SecurityConfig, WebMvcConfig(WebMvcConfigurer) 등
Spring Boot의 CORS 설정을 수정했는데
여전히 /api/proxy 500 Error가 발생했다.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/apis/**")
.allowedOrigins("http://localhost:3000") // Next.js 서버의 주소
.allowedMethods(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.OPTIONS.name())
.allowCredentials(true)
.maxAge(3600);
}
}
@Configuration
@EnableWebSecurity
public class HttpSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and() // CORS 허용
.authorizeRequests()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated();
}
}
@Bean
public FilterRegistrationBean<CORSFilter> corsFilter() {
FilterRegistrationBean<CORSFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new CORSFilter());
registrationBean.addUrlPatterns("/apis/**"); // 경로에 대한 CORS 필터 적용
return registrationBean;
}
다 설정 했는데...
No Response면 Spring Boot 서버가 요청을 차단했거나 응답을 반환하지 못하는 경우 아닌가...?
구글링에 디버깅에 콘솔 로그까지 다 찍어봤지만 원인을 찾을 수 없어 결국 ChatGPT에 소스 코드를 모두 복붙해 원인을 찾아내게 시켰다.
ChatGPT 찬스를 사용했음에도 불구하고
라고만 알려줬고, 여전히 Response는 Null이었다 ㅜㅜ
const response = await fetch('http://localhost:3000/api/proxy', request);
여기서 왜 계속 막히는 거냐구!!!
돌고 돌아 프록시 서버 로그를 확인하는 데에 집중했다.
export async function POST(req: NextRequest, res: NextResponse) {
const session = await auth();
const error = session?.error;
let formData = null;
let json = null;
try {
formData = await req.formData();
} catch (e) {
console.error('Not FormData');
}
try {
json = await req.json();
} catch (e) {
console.error('Not JSON');
}
if (session && !error) {
let response = null;
const requestInit = {
cache: 'no-cache' as RequestCache,
headers: {
'Authorization': Bearer ${session.access_token}
}
};
if (json) {
const { command: apiCommand, body } = json;
const apiItem = _.get(apiURLs, _.split(apiCommand, '.'));
const { method = 'GET', contentType = 'application/json', url } = apiItem;
let apiUrl = translateUrlJson(url, body);
_.set(requestInit, 'method', method);
_.set(requestInit.headers, 'Content-Type', contentType);
switch (method) {
case 'GET':
if (_.keys(body).length > 0) {
const querystring = new URLSearchParams(body).toString();
apiUrl += ?${querystring};
}
break;
case 'POST':
_.set(requestInit, 'body', JSON.stringify(body));
break;
}
response = await fetch(apiUrl, requestInit);
}
if (formData) {
const apiCommand = formData.get(API_COMMAND_KEY) as string;
formData.delete(API_COMMAND_KEY);
const apiItem = _.get(apiURLs, _.split(apiCommand, '.'));
const { method = 'POST', contentType = 'multipart/form-data', url } = apiItem;
const { url: apiUrl, data: updatedFormData } = translateUrlFormData(url, formData);
_.set(requestInit, 'method', method);
_.set(requestInit, 'body', updatedFormData);
response = await fetch(apiUrl, requestInit);
}
if (response != null) {
const responseContentType = response.headers.get('Content-Type');
if (responseContentType === 'application/zip') {
return new NextResponse(await response.blob(), {
status: response.status,
statusText: response.statusText,
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': 'attachment; filename="download.zip"'
}
});
}
return NextResponse.json(await response.json(), { status: response.status, statusText: response.statusText });
} else {
return NextResponse.json({ message: 'No Response' }, { status: 500 });
}
} else {
return NextResponse.json({ message: 'You must be logged in.' }, { status: 401 });
}
}
Spring과 통신하는 route.ts 코드인데
json이나 formData로 파싱하는 과정에서 오류가 발생할 수 있다는 글을 보았다.
그래서 아래와 같이 로그를 찍어 확인해보았는데
try {
const rawBody = await req.text();
console.log('[Raw Body]', rawBody);
json = await req.json();
} catch (e) {
console.error('Error parsing JSON:', e);
}
Error parsing JSON: TypeError: Body is unusable: Body has already been read 라는 에러가 떴다.
이 에러는 HTTP 요청의 body를 한 번 읽은 후에는 다시 읽을 수 없다는 스트림 처리의 특성에서 발생하는 문제라고 한다.
let json = await req.json();
let formData = await req.formData();
현재 위와 같이 파싱하고 있었는데
아래의 파싱으로 로직을 수정했다.
const body = await req.text();
let json = JSON.parse(body);
let formData = new URLSearchParams(body);
호출해보니 Spring Boot Controller 진입도 정상이고 데이터도 잘 가져오고 200 OK 떨어졌다!!!