일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- Binding
- typescript
- GitHub
- 오류
- MSSQL
- MVVM
- page
- Maui
- AnimationController
- JavaScript
- 애니메이션
- .NET
- Firebase
- React JS
- 플러터
- listview
- MS-SQL
- 함수
- Flutter
- HTML
- 닷넷
- 마우이
- 바인딩
- 자바스크립트
- 깃허브
- 리엑트
- 파이어베이스
- spring boot
- Animation
- db
- Today
- Total
개발노트
7. [ASP .NET Core] HttpClient 올바르게 사용하기 본문
.NET Framework 에서 사용했다면 아래 보통 아래와 같이 HttpClient 인스턴스를 생성하고 요청, 응답을 보내고 받는 방법을 사용했을 거야
HttpClient client = new HttpClient();
그냥 이렇게 사용했다면 어떤 것들이 문제되었을까?
MicroSoft에서는 기존 HttpClient의 어떤 문제들을 인지하였고 이것들을 고치기 위해 어떤 방안들을 제시했을지 같이 알아보자
.NET Core 2.1부터는 아래와 같이 사용할 수 있을거야
문제점 1. 소켓 고갈
- 소켓 고갈이 생기는 원인은 뭘까?
커넥션 Pool을 이용하지않고 새로운 인스턴스를 생성하기 때문에, 항상 새로운 연결을 생성하고 해제해
- HttpClient를 매번 new HttpClient()로 생성하면, 내부적으로 HttpClientHandler도 같이 생성됨.
- HttpClientHandler는 새로운 TCP 연결을 만들고, 요청을 처리한 후 해당 연결을 닫음.
따라서, 매번 새로운 연결을 맺고 해제하여 새로운 포트를 사용하고 이는 소켓 고갈문제가 발생시켜
- 왜? 해제 한다면 연결을 새로 생성하더라도 그 포트를 다시 사용할 수 있는 것 아니야?
응 아니야, 이유는
Dispose로 해제 하더라도 연결에 사용된 포트가 TIME_WAIT 상태로 넘어가기 때문에 해제된 포트를 바로 사용할 수 없어서 사용하지않고 있는 새로운 또 다른 포트를 사용해, 이 때문에 사용할 포트가 점점 줄어들고, 포트 고갈 문제가 생기는거지, 운영체제마다 동적으로 사용할 수 있는 포트의 수도 정해져있어 (윈도우10, 11 각각 달라)
- 연결이 닫혀도 운영체제(OS) 차원에서 바로 정리되지 않음.
- 연결이 닫힌 후에도 일정 시간 동안 TIME_WAIT 상태로 유지됨.
- 이 때문에 사용 가능한 포트가 빠르게 소진되면서 "소켓 고갈(Socket Exhaustion)" 문제가 발생함.
- OS에서 사용할 수 있는 동적 포트의 개수는 제한되어 있음(Windows 10/11, Linux마다 다름).
문제점 1. 소켓 고갈 해결 방법
- 그렇다면 소켓 고갈을 해결방법은 뭐야?
HttpClient를 static으로 선언하여 사용하거나, 싱글톤으로 사용하여, 하나의 인스턴스만 사용하게 하면 해결 돼
builder.Services.AddSigleton((serviceProvider) => new HttpClient())
문제점 2. DNS 업데이트 불가
하지만, 여기서 아직 해결되지않은 문제점이 있어 그것은 바로, DNS가 변경되었을 때 이 변경사항이 업데이트가 안된다라는 것이지
- 왜? DNS 변경을 감지하지 못해?
왜냐하면 하나의 인스턴스를 사용하게 되면 기존에 적용된 DNS를 가진 HttpClient 인스턴스만 사용하게 되니까,
App을 다시 시작하거나 변경할 로직을 따로 주지않는 이상 DNS가 업데이트 되지않아
- 앱이 실행될 때 HttpClient는 DNS 조회를 수행함.
- 이후 동일한 HttpClient 인스턴스를 계속 사용하면서 처음 조회된 DNS 정보를 유지함.
- 만약 백엔드 서버의 IP가 변경되었다면, 앱은 여전히 기존 IP를 향해 요청을 보내게 됨.
- 결국 DNS 업데이트가 반영되지 않아 장애가 발생할 가능성이 있음.
문제점 2. DNS 업데이트 하기
- 그렇다면, DNS 업데이트를 주기적으로 해줄 수 있는 방법이 뭐야?
App이 실행되는 동안 매번 동일한 HttpClient 인스턴스와 속성 값들을 계속 사용되지않게 하면 돼,
즉, 시한부처럼 이 인스턴스의 커넥션에 살아 있을 수 있는 시간을 부여하는 거지, 지정된 연결 지속시간이 땡하고 되면 이 연결은 끊어게되고 그 다음 요청이 오면 다시 새롭게 연결을 맺고 새로운 커넥션 정보를 다시 받아올거야
이걸 할 수 있는 방법은 SocketsHttpHandler을 사용하는 거야, 기존에 사용했던 HttpClientHandler에는 이 커넥션 유지시간을 부여할 수 있는 옵션이 없거든
HttpClient의 핸들러로 SocketsHttpHandler를 지정하고 SocketsHttpHandler 옵션 중 하나인 PooledConnectionLifetime으로 커넥션 유지 시간을 부여할 수 있어
builder.Services.AddSingleton((serviceProvider) => HttpClient(
new SocketsHttpHandler{
PooledConnectionLifetime = TimeSpan.FromMinutes(5) // 이 커넥션 지속시간은 5분 줄께
}
));
위처럼 하면 5분마다 DNS가 업데이트 되는 거지
- PooledConnectionLifetime을 설정하면 지정된 시간이 지나면 기존 연결을 닫고 새로운 연결을 생성함.
- 즉, DNS가 변경되더라도 일정 주기마다 새롭게 조회하게 됨.
- 하지만 이 방법도 싱글톤 패턴을 유지해야 하는 불편함이 있음.
더 좋은 방법은 없어?
싱글톤,Static 없이 사용할 수 있는 방법이 있어, 바로 HttpClientFactory 를 사용하는 것이지
HttpClientFactory 사용하기
HttpClientFactory의 역할은 HttpClient 풀(Pool)을 만들어주는 역할을 해
즉, 싱글톤처럼 하나의 인스턴스를 고정적으로 유지할 필요 없이, 적절히 풀에서 재사용되는 거지
이는 HttpClient 인스턴스 숫자를 관리해주고, 재사용될 수 있도록 도와주는 좀 더 나은 방식이지
우리는 HttpClient가 필요할 때 그냥 HttpClientFactory.CreateClient() 메서드를 호출해서 풀에 있는 HttpClient 를 사용하기만 하면되는거야
- HttpClient 인스턴스를 자동으로 관리
- 싱글톤처럼 하나의 인스턴스를 고정적으로 유지할 필요 없이, 적절히 풀에서 재사용됨.
- DNS 변경 자동 반영
- SocketsHttpHandler가 내부적으로 관리되므로, PooledConnectionLifetime을 적절히 조정하여 DNS가 변경되면 자동으로 업데이트됨.
- 각각의 HttpClient를 개별 구성 가능
- 특정 API 호출에 대해 개별적인 HttpClient 설정이 가능함.
// 서비스 등록
builder.Services.AddHttpClient();
// 사용 예시
public class MyService {
private readonly HttpClient _httpClient;
public MyService(IHttpClientFactory httpClientFactory) {
_httpClient = httpClientFactory.CreateClient(); // 풀에서 HttpClient를 가져옴
}
public async Task<string> GetDataAsync() {
var response = await _httpClient.GetAsync("https://example.com");
return await response.Content.ReadAsStringAsync();
}
}
- IHttpClientFactory를 주입받아서 CreateClient()를 호출하면 풀에서 HttpClient를 가져옴.
- 따라서, 불필요한 인스턴스 생성 없이 재사용되며, DNS 변경도 자동으로 반영됨.
이렇게 좋은 대안인 HttpClientFactory를 사용할 때도 주의할 점이 있어 바로 쿠키를 사용하는 경우야...
HttpClientFactory를 사용할 때 쿠키(CookieContainer) 를 사용하는 경우 다음과 같이 예외적인 상황이 발생할 수 있어.
HttpClientFactory 에서 쿠키(CookieContainer) 사용 시 문제점
HttpClientFactory는 내부적으로 HttpMessageHandler 풀(Pool) 을 관리하는데, HttpClient를 요청할 때마다 새로운 인스턴스를 반환하지만, 내부 핸들러는 재사용될 수 있어.
이게 왜 문제냐면?
- 쿠키를 저장하는 CookieContainer는 HttpMessageHandler에 묶여 있음
- HttpClientFactory.CreateClient() 로 HttpClient를 가져올 때, 새로운 인스턴스를 받더라도 이전 요청에서 사용된 핸들러(HttpMessageHandler)를 재사용할 수 있어.
- 따라서 쿠키가 의도치 않게 공유될 가능성이 있음.
- 쿠키를 분리해서 관리하고 싶어도 기본 HttpClientFactory에서는 불가능
- HttpClientFactory로 생성한 HttpClient는 내부적으로 SocketsHttpHandler를 사용하지만, 이 핸들러는 쿠키를 따로 저장하지 않음.
- 즉, CookieContainer를 명시적으로 설정하려면 기본 HttpClientFactory가 아니라 커스텀 핸들러를 사용한 HttpClientFactory를 설정해야 함.
- 쿠키를 개별적으로 관리하는 방법은 뭐가 있어?
HttpClientFactory에서 쿠키 컨테이너 사용하기
쿠키를 개별적으로 관리하려면 HttpClientFactory에서 HttpMessageHandler를 커스터마이징해서 사용해야 해.
아래처럼 IHttpClientFactory에 등록할 때 SocketsHttpHandler에 CookieContainer를 직접 설정할 수 있어.
services.AddHttpClient("WithCookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new SocketsHttpHandler
{
UseCookies = true,
CookieContainer = new CookieContainer(),
PooledConnectionLifetime = TimeSpan.FromMinutes(5) // 5분마다 새로운 연결
};
});
이렇게 하면 "WithCookies"라는 이름의 HttpClient를 요청할 때마다 쿠키를 저장할 CookieContainer가 유지됨.
사용할 때는 아래처럼 하면 돼
var client = httpClientFactory.CreateClient("WithCookies");
- HttpClientFactory를 사용하면 DNS 업데이트가 가능하고, HttpClient 풀을 관리해 성능도 최적화할 수 있음
- 하지만 쿠키(CookieContainer)가 필요하다면 기본 HttpClientFactory를 그대로 쓰면 안 되고, ConfigurePrimaryHttpMessageHandler()를 사용해 커스텀 핸들러를 설정해야 함
- 쿠키를 개별적으로 관리하고 싶다면 요청마다 CookieContainer를 따로 생성해서 사용해야 함
'서버 개발 > ASP .NET Core' 카테고리의 다른 글
6. [ASP .NET Core] Dapper (0) | 2025.01.27 |
---|---|
5. [ASP .NET Core] IList 사용하기 (0) | 2024.11.17 |
4. [ASP .NET Core] 의존성 역전 원칙 (DIP) (1) | 2024.11.17 |
3. [ASP .NET Core] 의존성 주입(DI), IoC 란 무엇인가? (with Simple Injector) (0) | 2024.11.17 |
2. [ASP .NET Core] 간단 API 만들기(with Dapper) (0) | 2024.11.10 |