백엔드 개발에서 마이크로서비스 아키텍처는 점점 더 많은 관심을 받고 있습니다. 특히 Node.js는 비동기 처리와 높은 성능 덕분에 많은 개발자들이 선호하는 언어입니다. 하지만 마이크로서비스 간의 통신은 때때로 다양한 오류를 발생시킬 수 있습니다. 이번 글에서는 Node.js에서 마이크로서비스 간 통신 시 발생할 수 있는 오류와 그 해결 방법에 대해 다뤄보겠습니다.
마이크로서비스 간 통신의 기본
마이크로서비스 아키텍처에서는 각 서비스가 독립적으로 동작하면서 RESTful API, gRPC, 메시지 브로커 등의 방법으로 서로 통신합니다. 이러한 통신 과정에서 발생할 수 있는 일반적인 오류는 다음과 같습니다.
- 네트워크 오류: 서비스 간 통신이 실패하거나 지연되는 경우
- 타임아웃: 응답이 지정된 시간 내에 오지 않는 경우
- 서버 오류: 요청을 처리하는 과정에서 500과 같은 서버 오류가 발생하는 경우
- 인증/인가 오류: 필요한 인증 정보가 누락되거나 잘못된 경우
이제 이러한 오류를 해결하기 위한 구체적인 방법을 살펴보겠습니다.
네트워크 오류 처리
네트워크 오류는 매우 일반적이며, 이 오류를 처리하기 위해 기본적인 재시도 메커니즘을 구현하는 것이 좋습니다. 예를 들어, Axios를 사용하는 경우 다음과 같이 재시도 로직을 추가할 수 있습니다.
const axios = require('axios');
async function fetchData(url, retries = 3) {
try {
const response = await axios.get(url);
return response.data;
} catch (error) {
if (retries > 0) {
console.log(Retrying... ${retries} attempts left);
return fetchData(url, retries - 1);
} else {
throw new Error('Failed to fetch data after multiple attempts');
}
}
}
fetchData('http://example.com/api/data')
.then(data => console.log(data))
.catch(err => console.error(err.message));
위 코드에서는 Axios를 사용하여 API 요청을 보내고, 오류가 발생할 경우 재시도하는 로직을 구현했습니다.
타임아웃 설정
타임아웃 설정은 네트워크 요청이 너무 오래 걸릴 경우 적절히 처리할 수 있도록 도와줍니다. Axios에서는 timeout 옵션을 통해 설정할 수 있습니다.
const axios = require('axios');
async function fetchDataWithTimeout(url) {
try {
const response = await axios.get(url, { timeout: 5000 }); // 5초 타임아웃 설정
return response.data;
} catch (error) {
if (error.code === 'ECONNABORTED') {
throw new Error('Request timed out');
}
throw new Error('An error occurred: ' + error.message);
}
}
fetchDataWithTimeout('http://example.com/api/data')
.then(data => console.log(data))
.catch(err => console.error(err.message));
이 코드는 5초 이내에 응답을 받지 못할 경우 타임아웃 오류를 발생시킵니다.
서버 오류 처리
서버 오류는 주로 서버측의 문제로 인해 발생하지만 클라이언트에서도 적절한 처리가 필요합니다. HTTP 상태 코드가 500일 경우 사용자에게 적절한 메시지를 전달하거나, 대체 경로로 요청을 전환할 수 있습니다.
const axios = require('axios');
async function fetchDataWithErrorHandling(url) {
try {
const response = await axios.get(url);
if (response.status >= 500) {
console.error('Server error occurred');
// 대체 서비스로 요청 전환 로직 추가
}
return response.data;
} catch (error) {
console.error('An error occurred: ', error.message);
throw error;
}
}
fetchDataWithErrorHandling('http://example.com/api/data')
.then(data => console.log(data))
.catch(err => console.error(err.message));
위 코드에서 서버 오류가 발생할 경우, 별도의 로직을 통해 대체 서비스로의 요청을 전환하는 방안을 고려할 수 있습니다.
인증 및 인가 오류
마이크로서비스 간의 통신에서는 각 서비스의 인증 및 인가를 고려해야 합니다. JWT(JSON Web Token)를 사용하는 경우, 토큰이 만료되었거나 잘못된 경우를 처리해야 합니다.
const axios = require('axios');
async function fetchDataWithAuth(url, token) {
try {
const response = await axios.get(url, {
headers: {
Authorization: Bearer ${token}
}
});
return response.data;
} catch (error) {
if (error.response && error.response.status === 401) {
throw new Error('Unauthorized: Invalid or expired token');
}
throw new Error('An error occurred: ' + error.message);
}
}
const token = 'your_jwt_token_here';
fetchDataWithAuth('http://example.com/api/protected-data', token)
.then(data => console.log(data))
.catch(err => console.error(err.message));
이 코드에서는 JWT를 사용하여 인증을 처리하고, 만약 401 오류가 발생할 경우 적절한 오류 메시지를 출력합니다.
마무리
Node.js에서 마이크로서비스 간 통신 시 발생할 수 있는 다양한 오류와 그 해결 방법에 대해 살펴보았습니다. 각 오류에 대해 효과적으로 대처하는 방법을 적용하면, 시스템의 안정성을 향상시킬 수 있습니다. 특히, 비동기 처리와 고성능을 자랑하는 Node.js의 특성을 잘 활용한다면 마이크로서비스 아키텍처의 장점을 극대화할 수 있습니다. 개발 과정에서 발생하는 오류를 신속하게 해결하는 것이 백엔드 개발자의 중요한 역할임을 잊지 마세요.