「이 시스템, MSMQ를 쓰고 있는데 폐지됐다고 들었습니다. 바로 이전하지 않으면 안 되나요」── 레거시 이전 상담에서 최근 1년 정도 반복해서 듣는 질문입니다.
답은 조금 뒤틀려 있습니다. MSMQ(Microsoft Message Queuing)는 폐지는커녕 공식적으로 비권장(deprecated)이 된 적조차 없습니다. 2026년 7월 시점에 마이크로소프트의 비권장 기능 목록에 MSMQ의 이름은 없고, 현행 Windows에도 옵션 기능으로 동봉되어 있습니다. 그런데도 현장의 감각으로 「끝난 기술」로 느껴지는 것은 .NET 쪽의 공식 관리 API(managed API)가 막혀 있기 때문입니다. MSMQ를 다루는 표준 라이브러리 System.Messaging은 .NET Framework에만 존재하며, .NET(Core 이후)으로는 이식되지 않았습니다.
즉 MSMQ는 「내일 멈추는」 기술이 아니라, 「앱을 .NET으로 올리려는 순간 벽이 되는」 기술입니다.
이 글의 대상 독자는 MSMQ를 사용한 기존 시스템의 유지보수·이전을 담당하는 개발자와, 그 판단을 요구받는 정보 시스템 부서 담당자입니다. 「폐지됐다더라」는 소문의 진위를 확인하고, 이전 여부를 자사 환경의 조건에 따라 스스로 결정할 수 있는 상태가 되는 것을 목표로 합니다.
이 글에서는 소문과 사실을 구분한 다음, 계속 사용할지·이전할지의 판단과 이전 대상의 선택 방법을 정리합니다. VB6나 .NET Framework로부터의 이전 자체는 「VB6에서 .NET으로의 이전 실무」「.NET Framework→.NET 이전 전 체크리스트」에서 다루고 있으므로, 이 글은 큐 부분에 한정합니다.
1. 먼저 결론
- MSMQ는 공식적으로 비권장 상태가 아닙니다. Windows 클라이언트의 비권장 기능 목록에도, Windows Server의 삭제·개발 종료 기능 목록에도 실려 있지 않습니다(2026년 7월 시점).12
- 그러나 .NET의 공식 관리 API는 없습니다. System.Messaging이 대상으로 하는 것은 .NET Framework 1.1~4.8.1뿐이며,3 .NET 이전 시의 호환 수단인 Windows 호환 팩에도 포함되어 있지 않습니다.4 네이티브 Win32 API를 P/Invoke로 호출하는 길은 남아 있지만, 어디까지나 연명 수단의 위치입니다(3장).
- CoreWCF에 의한 이식이 있지만, 대상은 큐를 경유하는 WCF 서비스(수신 측)의 이전에 한정되며, 조건을 확인하고 사용해야 하는 경로입니다. CoreWCF 자체에는 마이크로소프트의 지원 정책이 있는 반면, MSMQ 트랜스포트(CoreWCF.MSMQ)의 구현은 .NET Framework판 System.Messaging의 커뮤니티 이식에 의존하고 있습니다.5
- 판단의 축은 「앱을 .NET으로 올릴 것인가」입니다. 올린다면 큐도 함께 이전하는 것이 원칙입니다(P/Invoke나 CoreWCF로 연명하는 길도 있지만, 유지보수 부담을 떠안는 판단이 됩니다). 올리지 않는다(동결)면, .NET Framework 4.8이 OS 구성 요소로서 지원되는 동안은 계속 가동시키는 계획을 세울 수 있습니다.6
- 이전 대상의 제1후보는 메시지 브로커가 아니라 데이터베이스의 테이블 큐입니다. MSMQ+분산 트랜잭션(DTC)으로 지키던 정합성은, 업무 DB와 동일 트랜잭션으로 처리할 수 있는 테이블 큐 쪽이 더 순순히 대체할 수 있습니다(5장).
- 메시지 형식의 재고 조사를 가장 먼저 하십시오. BinaryFormatter 계열의 직렬화는 .NET 9에서 런타임에서 삭제되었으며,7 오래된 형식 그대로 신구 병존시키면 막힙니다(6장).
2. MSMQ를 30초 만에
MSMQ는 Windows에 동봉되어 온 메시지 큐 기반입니다. 애플리케이션은 큐에 메시지를 쓰고, 다른 애플리케이션(같은 머신이든 다른 머신이든)이 편한 타이밍에 꺼냅니다. 특징은 다음 3가지로 집약됩니다.
- store-and-forward: 수신처가 다운되어 있어도 메시지는 로컬에 축적되어, 복구 후에 전달됩니다. 거점 간의 불안정한 회선에 강합니다. 다만 이 영속성은 무조건적인 것이 아니라, 기본값인 고속(express) 메시지는 메모리상에 머물 수 있어 MSMQ 서비스나 머신 재시작 시 소실됩니다. 디스크에 영속화되는 것은 송신 측에서 Recoverable(회복 가능)을 지정한 메시지이거나 트랜잭션 메시지뿐입니다.
- 트랜잭션: 큐 조작을 트랜잭션으로 만들 수 있으며, MS DTC(분산 트랜잭션 코디네이터)와 조합하면 「큐에서의 꺼내기」와 「데이터베이스 갱신」을 하나의 분산 트랜잭션으로 확정할 수 있습니다.
- OS 동봉: 추가 미들웨어 도입 없이 사용할 수 있었기 때문에, 2000년대 업무 시스템, 특히 수발주 데이터 연계, 장표(帳票)의 비동기 처리, 제조 현장의 공정 간 연계 등에서 널리 채택되었습니다.
이 「OS 동봉이면서, 트랜잭션이 작동하고, 오프라인에 강하다」는 조합이 우수했기 때문에, 지금도 현역으로 남아 있습니다. 이전 대상을 생각할 때도, 이 3가지 중 어느 것을 실제로 사용하고 있는가가 판단의 중심이 됩니다.
이 글에서 사용하는 용어 최소 세트
5장의 판단표와 6장의 재고 조사는 다음 용어를 전제로 합니다. 여기서 한 번에 짚고 넘어가십시오.8
- 프라이빗 큐 ── 디렉터리 서비스(Active Directory)에는 게시하지 않고, 해당 컴퓨터에만 등록되는 큐입니다.
.\private$\큐이름과 같은 형식으로 지정합니다. 이에 대응하는 퍼블릭 큐는 디렉터리 서비스에 등록되어 도메인 내에서 검색할 수 있습니다. 중소 규모 업무 시스템에서 사용되는 것은 대부분 프라이빗 큐입니다. - express 메시지 ── 기본 송신 모드입니다. 전송 중에도 전송 후에도 메모리상에 놓이기 때문에 고속이지만, MSMQ 서비스나 머신이 멈추면 소실됩니다.
- recoverable(회복 가능) 메시지 ── 송신 측과 중계하는 각 컴퓨터에서 디스크에 기록되고, 대상 큐에서도 디스크상에 보관되는 모드입니다. 재시작을 넘어 남습니다. 송신 측이 명시적으로 지정합니다.
- 트랜잭션 큐 ── 트랜잭션 메시지만을 다루는 큐입니다. 큐 생성 시에 결정되며, 나중에 변경할 수 없습니다(6장의 이전 절차에 영향을 미칩니다).
- DTC(분산 트랜잭션 코디네이터) ── 여러 리소스(MSMQ의 큐와 데이터베이스 등)에 걸친 트랜잭션을 조정하는 Windows의 서비스입니다. 「큐에서의 꺼내기」와 「DB 갱신」을 하나로 묶어 확정시키는 구조로, 5장의 이전 판단에서 가장 큰 쟁점이 됩니다.
- 저널 큐 / 배달 불가(데드레터) 큐 ── MSMQ가 자동 생성하는 시스템 큐입니다. 저널은 「송신했거나 꺼낸 메시지의 사본」을, 배달 불가 큐는 「전달되지 못한 메시지」를 쌓아 둡니다. 방치하면 용량을 잡아먹기 때문에, 동결 운용에서는 감시 대상이 됩니다(7장).
3. 사실 관계 ── 「폐지되었다」는 정확하지 않다
「MSMQ는 사용할 수 있는가」라는 질문이 혼란스러운 것은, 레이어에 따라 답이 다른데도 하나로 뭉뚱그려 이야기되기 때문입니다. 먼저 전체상을 표 하나로 정리해 두겠습니다.
| 레이어 | 현재 상태 | 실무상의 의미 |
|---|---|---|
| OS 기능(Windows의 옵션 기능) | 존속. 현행 Windows 클라이언트/Server에 동봉되어 있으며, 비권장 선언도 나오지 않음12 | 활성화하면 지금도 동작함. OS의 지원 기간 동안은 계속 사용할 수 있음 |
Win32 네이티브 API(MQSendMessage 등) |
문서화되어 있으며 이용 가능9 | P/Invoke로 .NET에서 호출하는 길은 남아 있음. 다만 포맷터나 트랜잭션 연계는 직접 작성하여 유지보수해야 함 |
| .NET Framework의 System.Messaging | 이용 가능. 다만 대상은 .NET Framework 1.1~4.8.13 | 기존 시스템은 여기서 동작 중임. 이 앞이 없음 |
| .NET(Core 이후)의 공식 관리 API | 존재하지 않음. Windows 호환 팩에도 포함되지 않음4 | 앱을 .NET으로 올리는 순간, 큐 부분을 다시 만들어야 함 |
| WCF의 MSMQ 바인딩 → CoreWCF.MSMQ | 커뮤니티 주도의 이식이 존재. CoreWCF 자체에는 지원 정책이 있지만, MSMQ 구현은 System.Messaging의 커뮤니티 이식에 의존5 | 큐를 경유해 호출되는 WCF 서비스(수신 측)의 연명에는 사용할 수 있지만, 송신 측의 대체도 범용 큐 API도 아님 |
| 신규 채택 | 권장할 수 없음 | 공식적인 미래의 길이 없기 때문 |
이하, 이 표 각 행의 근거를 순서대로 확인하겠습니다.
첫째, MSMQ는 비권장 목록에 실려 있지 않습니다. Windows 클라이언트의 「Deprecated features」 목록을 보면 NTLM이나 VBScript, WordPad는 실려 있지만 MSMQ 항목은 없습니다.1 Windows Server 쪽의 「Features Removed or No Longer Developed」 목록에서도, Windows Server 2025 탭을 포함해 MSMQ는 언급되어 있지 않습니다.2 비권장(deprecated)이란 「적극적인 개발을 중단했다」는 공식 선언인데, 그 선언조차 나오지 않은 것이 현재 위치입니다.
둘째, System.Messaging은 .NET Framework에서 멈춰 있습니다. 레퍼런스의 대상 버전은 .NET Framework 1.1부터 4.8.1까지이며, .NET(Core 이후) 버전은 존재하지 않습니다.3 .NET Framework 전용 API의 수용처인 Windows 호환 팩(Microsoft.Windows.Compatibility)은 레지스트리·WMI·Windows 서비스·EventLog 등 약 2만 개의 API를 제공하지만, 그 기술 영역 목록에 메시징(System.Messaging)은 포함되어 있지 않습니다.4 정확히 말하면, 막혀 있는 것은 관리 API(managed API)입니다. MSMQ의 Win32 네이티브 API(MQSendMessage, MQReceiveMessage 등)는 지금도 문서화되어 있어, .NET에서 P/Invoke로 호출하는 것 자체는 가능합니다. 다만 System.Messaging이 흡수하고 있던 포맷터나 트랜잭션 연계를 직접 만든 래퍼로 작성해 계속 유지보수해야 하므로, 「계속 사용하기 위한 본명(本命)」이 아니라 「어떻게든 남겨야 할 경우의 연명 수단」의 위치입니다.
셋째, WCF의 MSMQ 연계도 같은 벽 안에 있습니다. .NET Framework의 WCF에는 MSMQ를 밑단으로 사용하는 바인딩이 있었지만, 이 경로를 모던 .NET에서 재현하려 하면 커뮤니티 주도의 CoreWCF에 다다르게 됩니다. CoreWCF는 큐 계열 트랜스포트의 일환으로 MSMQ 대응 패키지(CoreWCF.MSMQ)를 공개하고 있지만, 그 구현은 .NET Framework판 System.Messaging 라이브러리의 커뮤니티 이식에 대한 의존을 명시하고 있습니다.5 동작하느냐로 말하면 동작하고, CoreWCF 자체에는 마이크로소프트의 공식 지원 정책이 마련되어 있으므로 「완전히 근본 없는 라이브러리」인 것도 아닙니다.5 다만 MSMQ 트랜스포트가 의존하는 System.Messaging의 커뮤니티 이식까지 같은 취급을 받는지는 별개의 문제입니다. 채택한다면, 사용할 버전이 지원 정책의 대상인지와 이 의존 부분의 취급을 확인한 뒤에 판단하십시오.
이상이 앞서 제시한 일람표 각 행의 근거입니다. 「MSMQ가 폐지된」 것이 아니라, 「모던 .NET에서 MSMQ에 이르는 공식적인 길이 없는」 것입니다── 이 구별을 갖고 있으면 사내 논의가 어긋나지 않게 됩니다.
4. 「동작하고 있는데도 문제」의 정체
MSMQ가 얽힌 시스템 상담은 대개 장애가 아니라 이전 견적에서 시작됩니다. 문제의 정체는 MSMQ 그 자체가 아니라, MSMQ가 앱 전체를 .NET Framework에 묶어 두는 닻이 된다는 점입니다.
.NET Framework 4.8은 Windows의 구성 요소로 취급되며, 설치된 OS의 라이프사이클에 따라 지원됩니다.6 그래서 「계속 동작할 것인가」라는 질문에는 「당분간은 계속 동작한다」고 답할 수 있습니다. 그럼에도 다음 항목들은 시간이 지날수록 확실히 무거워집니다.
- 사람을 구할 수 없고, 인수인계할 수 없다. System.Messaging과 DTC를 설명할 수 있는 기술자는 해마다 줄고 있습니다. 「소스도 자료도 없는 시스템의 유지보수」에서 다룬 구도의 전형적인 예입니다.
- 런타임과 새 라이브러리의 혜택을 받을 수 없다. 새로운 C# 구문 중에는 컴파일러 업데이트만으로 .NET Framework에서도 쓸 수 있는 것이 많지만, 런타임 쪽 성능 개선이나 새 표준 라이브러리는 사용할 수 없고, .NET Framework를 지원 대상에서 제외해 나가는 최근 패키지들도 점점 선택할 수 없게 됩니다.
- 분산 트랜잭션이 이전의 최대 난관이 된다. MSMQ+DTC의 「큐 꺼내기와 DB 갱신을 원자적으로」라는 설계는 클라우드 계열의 큐 서비스에서는 재현할 수 없습니다. 이 부분을 방치한 채 주변만 .NET화하면, 마지막에 가장 단단한 심(芯)이 남게 됩니다.
- 직렬화 형식이라는 시한폭탄. 오래된 시스템의 메시지 본문은 바이너리 직렬화로 되어 있는 경우가 있으며, BinaryFormatter는 .NET 9에서 런타임으로부터 삭제되었습니다.7 이전 시에는 메시지 형식 자체를 재검토해야 합니다.
즉 「MSMQ는 언제까지 사용할 수 있는가」라는 질문에 대한 실무적인 답은, 「OS가 지원하는 한 동작하지만, 이전의 난이도는 기다릴수록 올라간다」입니다. 판단을 미루더라도, 다음 장의 판단표에서 「어디가 난관인가」만큼은 먼저 파악해 두어야 합니다.
5. 이전 대상 판단표
이전 대상은 「MSMQ의 후속 제품이 무엇인가」가 아니라, 실제로 사용하고 있는 성질별로 선택합니다.
5.1. 먼저 요건을 4가지 질문으로 정리한다
- 큐의 상대(수신 측 처리)는 자사 데이터베이스를 갱신하는 처리인가?
- 분산 트랜잭션(DTC)을 사용하고 있는가?(트랜잭션 큐+DB 갱신)
- 송신 측과 수신 측은 다른 머신·다른 거점인가? 오프라인 내성(store-and-forward)을 정말로 사용하고 있는가?
- 메시지량은 실제로 어느 정도인가?(많은 업무 시스템에서는 하루 수천~수만 건이며, 이는 어떤 선택지로도 여유롭게 처리됩니다)
5.2. 판단표
| 사용하고 있는 성질 | 제1후보 | 이유와 주의 |
|---|---|---|
| 동일 시스템 내의 비동기 처리(수신 측은 DB 갱신) | DB의 테이블 큐 | 「메시지 꺼내기」와 「업무 데이터 갱신」을 동일 로컬 트랜잭션으로 확정할 수 있어 DTC가 불필요해진다. 기존의 백업·감시·운용을 그대로 사용할 수 있다. 또한 송신 측에서도 「업무 데이터 갱신과 메시지 행 추가」를 동일 트랜잭션으로 만들 수 있으며, 이것이 이른바 Outbox 패턴이다 |
| DTC로 큐와 DB 갱신을 원자적으로 만들고 있다 | DB의 테이블 큐 | 분산 트랜잭션을 로컬 트랜잭션으로 대체하는 것이 이전의 본질이다. 클라우드 계열 큐는 DTC에 참여할 수 없으므로, 이 요건이 있는 한 브로커 계열은 우회로가 된다 |
| 복수 시스템·복수 언어 간의 느슨한 결합 연계 | RabbitMQ(온프레미스) / Azure Service Bus(클라우드 가능) | 배포의 팬아웃이나 라우팅은 브로커의 장기다. RabbitMQ는 자체 운용 부담(이중화·업데이트)을 견적해 둘 것 |
| 거점 간 오프라인 내성(store-and-forward) | Azure Service Bus 등+재전송 설계, 또는 거점 측 테이블 큐+동기화 | MSMQ의 「송신 측 로컬에 쌓아 두었다가 나중에 전달」을 투명하게 대체하는 구조는 많지 않다. 송신 측에서 쌓아 두는 책임을 앱 쪽에 명시적으로 부여하는 설계로 바꾼다 |
| 동일 프로세스 내의 생산자/소비자 | .NET의 Channels 등 인메모리 큐 | 애초에 프로세스 간 큐가 불필요했던 사례다. 프로세스 내에서 완결시키고, 영속성이 필요하다면 테이블 큐로 |
| Windows 서비스 간의 프로세스 간 통신으로만 사용하고 있다 | 명명된 파이프 등의 IPC | 다만 대체할 수 있는 것은 양쪽이 항상 동시에 동작하고 있는 경우뿐이다. 수신 측이 정지 중이어도 MSMQ는(express 메시지라도) 송신을 받아들여 나중에 전달하지만, 파이프는 즉시 실패한다. 정지 중의 축적에 의존하고 있다면, 재전송·버퍼를 앱 쪽에 구현하거나 큐를 남긴다. 선택 방법은 「Windows의 IPC 판단표」로 |
5.3. 테이블 큐의 최소 구현
「테이블 큐」라는 말을 들어도 감이 안 온다는 목소리가 많으므로, 최소 형태를 제시합니다. 먼저 테이블 정의입니다(SQL Server).
CREATE TABLE dbo.JobQueue (
Id BIGINT IDENTITY(1,1) PRIMARY KEY,
Payload NVARCHAR(MAX) NOT NULL, -- 본문. JSON으로 보관
Status TINYINT NOT NULL DEFAULT 0, -- 0:미처리 1:처리중 2:완료
EnqueuedAt DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
StartedAt DATETIME2 NULL, -- 처리 중인 채로 죽은 행 회수에 사용
RetryCount INT NOT NULL DEFAULT 0
);
CREATE INDEX IX_JobQueue_Status ON dbo.JobQueue (Status, Id);
꺼내기는 1건만 「처리 중」으로 표시하면서 본문을 받습니다. READPAST 를 붙임으로써 다른 워커가 잠그고 있는 행을 기다리지 않고 건너뛸 수 있습니다(여러 프로세스에서 동시에 돌릴 수 있습니다).
WITH next_job AS (
SELECT TOP (1) *
-- READCOMMITTEDLOCK은 READ_COMMITTED_SNAPSHOT이 ON인 DB에서 필요(후술).
-- OFF인 DB에서는 기본 동작과 동일하므로, 붙인 채로 두면 양쪽 모두에 통한다
FROM dbo.JobQueue WITH (READPAST, UPDLOCK, READCOMMITTEDLOCK)
WHERE Status = 0
ORDER BY Id -- 투입 순서대로 꺼낸다. 이것이 "큐"이기 위한 조건
)
UPDATE next_job
SET Status = 1, StartedAt = SYSUTCDATETIME()
OUTPUT inserted.Id, inserted.Payload;
READPAST 는 READ_COMMITTED_SNAPSHOT 이 ON 인 데이터베이스에서는 그대로는 사용할 수 없습니다. 공식 문서는 「READ_COMMITTED_SNAPSHOT 이 ON 이고, 세션의 격리 수준이 READ COMMITTED 일 때(또는 READCOMMITTED 힌트를 병용하고 있을 때), READPAST 는 지정할 수 없다」고 명시하며, 그 경우의 대처로 READCOMMITTEDLOCK 힌트를 함께 지정할 것을 제시하고 있습니다.10 기본 격리 수준은 READ COMMITTED 이므로, READ_COMMITTED_SNAPSHOT 을 활성화한 것만으로도, 이 꺼내기는 「다른 워커의 행을 건너뛴다」는커녕 구문 자체가 오류가 됩니다. Azure SQL Database에서는 기본값이 ON 이고, 온프레미스에서도 읽기 시 블로킹 대책으로 활성화해 둔 환경이 드물지 않습니다. 이전 대상 DB가 어느 쪽인지 먼저 확인하십시오.
SELECT is_read_committed_snapshot_on FROM sys.databases WHERE name = DB_NAME();
흔히 보는 WITH (READPAST, UPDLOCK, ROWLOCK) 에서 ROWLOCK 을 뺀 것은, ROWLOCK 과 READCOMMITTEDLOCK 이 같은 「세분성 힌트」 그룹이어서, 하나의 테이블에 둘 다 지정할 수 없기 때문입니다.10 READPAST 가 건너뛸 수 있는 것은 행 잠금뿐이고 페이지 잠금은 건너뛸 수 없으므로 ROWLOCK 을 명시하고 싶어지지만, TOP (1) 의 인덱스 시크로 가져오는 1행에 대해 에스컬레이션이 일어나는 일은 실질적으로 없습니다. READ_COMMITTED_SNAPSHOT 이 OFF 임을 알고 있고 ROWLOCK 을 명시하고 싶다면, READCOMMITTEDLOCK 쪽을 빼십시오.
ORDER BY 를 생략하고 UPDATE TOP (1) 이라고 쓰면, 어느 행이 선택될지는 정해져 있지 않습니다. UPDATE 의 TOP 은 대상 행을 정렬하지 않는다고 공식 문서에 명시되어 있으며, (Status, Id) 인덱스가 있어도 꺼내는 순서를 보장하지 않습니다.11 투입 순서대로 처리되는 것을 전제로 하는 연계에서는, 오래된 작업이 계속 뒤로 밀리는 형태의 결함이 됩니다.
다만 ORDER BY Id 가 맞춰 주는 것은 꺼내는 순서뿐입니다. 처리가 끝나는 순서는 맞춰지지 않습니다. READPAST 는 「다른 워커가 잠그고 있는 행을 건너뛴다」는 지정이므로, 워커 A가 작업 1을 잡고 오래 걸리는 동안 워커 B는 작업 2를 잡아 먼저 커밋할 수 있습니다.
| 맞춰지는 것 | 맞춰지지 않는 것 |
|---|---|
어느 행을 먼저 꺼내는가(ORDER BY Id) |
어느 행이 먼저 업무 데이터에 반영되는가 |
「같은 수주 번호에 대한 갱신 순서가 뒤바뀌면 곤란하다」처럼 반영 순서에 의미가 있는 연계에서는 이것만으로는 부족합니다. 취할 수 있는 형태는 다음 둘 중 하나입니다.
- 소비자를 1개로 만든다. 병렬도를 포기하고 순서를 확보합니다. 처리량이 충분하다면 이것이 가장 단순하고 잘 깨지지 않는 형태입니다
- 키 단위로 나눈다. 수주 번호 등의 키로 워커를 고정하거나, 「같은 키의 행이 처리 중이면 꺼내지 않는다」는 조건을 꺼내기 쪽에 추가하여, 키 안에서만 순서를 보장합니다. 키를 넘나드는 순서는 포기합니다
둘 다 취하지 않는다면, 병렬로 돌리는 시점에 전체 FIFO는 없다는 사실을 이전 설계에 명시해 두십시오. MSMQ에서 넘어오는 경우 이 부분은 특히 간과되기 쉽습니다. MSMQ의 큐도 병렬로 수신하면 같은 일이 일어나지만, 「큐니까 순서대로」라는 이해를 그대로 가지고 넘어오면, 테이블 큐로 바꾼 순간 순서 문제가 생긴 것처럼 보입니다.
수신 측 워커는 이것을 업무 처리와 같은 로컬 트랜잭션 안에서 돌리기만 하면 됩니다(C#, 의사 코드).
while (!stoppingToken.IsCancellationRequested)
{
using var tx = connection.BeginTransaction();
var job = DequeueOne(connection, tx); // 위의 UPDATE ... OUTPUT
if (job is null)
{
tx.Commit();
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); // 비어 있으면 폴링 간격만큼만 대기
continue;
}
ApplyBusinessData(connection, tx, job); // ← 업무 데이터 갱신(원래 하고 싶은 일)
MarkDone(connection, tx, job.Id); // Status = 2
tx.Commit(); // "꺼내기"와 "업무 갱신"이 이 한 줄에서 동시에 확정된다
}
여기가 MSMQ+DTC를 대체하고 있는 지점입니다. MSMQ에서는 「큐에서의 꺼내기」와 「DB 갱신」이 별개의 리소스였기 때문에, 둘을 하나로 묶는 데 분산 트랜잭션(DTC)이 필요했습니다. 큐가 DB의 테이블이라면 양쪽 모두 같은 데이터베이스에 대한 갱신이므로 평범한 로컬 트랜잭션으로 충분합니다. RabbitMQ나 Azure Service Bus를 도입하면 이 성질은 사라지고(큐와 DB가 다시 별개의 리소스로 돌아가고), 멱등성이나 재전송을 앱 쪽에서 직접 구현해야 하게 됩니다.
실제 운용에서 추가할 필요가 있는 것은 다음 3가지 정도입니다.
- 처리 중인 채로 죽은 행의 회수.
Status = 1이면서StartedAt이 일정 시간보다 오래된 행을Status = 0으로 되돌리는 복구 처리를 정기 실행에 넣습니다. - 재시도 상한과 대피.
RetryCount가 상한을 넘은 행은 별도 테이블(또는Status = 9)로 옮깁니다. MSMQ의 배달 불가 큐에 해당하는 보관소입니다. - 완료 행 정리.
Status = 2인 행을 정기적으로 삭제·아카이브합니다. 방치하면 테이블이 비대해지고 인덱스 효율이 떨어집니다.
송신 측에서는 업무 데이터 갱신과 메시지 행의 INSERT 를 같은 트랜잭션에 넣습니다(Outbox 패턴). 이렇게 하면 「업무 데이터는 갱신되었는데 메시지는 나가지 않았다」는 불일치도 사라집니다.
강조하고 싶은 것은, 중소 규모 업무 시스템에서는 테이블 큐가 제1후보가 되는 경우가 많다는 점입니다. 메시지 큐 제품 비교 기사는 「RabbitMQ vs Kafka vs Service Bus」라는 형태가 되기 쉽지만, MSMQ 세대의 시스템이 큐에 실어 나르는 것은 대부분 「같은 DB를 갱신하는 비동기 작업」이며, 이는 DB의 테이블과 폴링(또는 알림)만으로 충분히, 게다가 더 단순하게 실현할 수 있습니다. 새로운 미들웨어를 하나 늘리는 것의 운용 비용(감시·이중화·패치·담당자 교육)은 중소 규모 체제에서는 특히 무겁게 다가옵니다.
6. 이전 실무 절차
진행 방식은 레거시 이전의 정석대로 「관찰한 뒤에 움직인다」입니다.
-
의존 관계 재고 조사. 코드에서
System.Messaging참조와MessageQueue생성 지점을 찾아냅니다. 아울러 WCF 구성 파일의netMsmqBinding/msmqIntegrationBinding, 네이티브 API(MQSendMessage등의MQ*함수)나 COM 경유 호출도 검색하십시오. System.Messaging을 참조하지 않아도 MSMQ에 의존하는 경로는 있을 수 있습니다. 확인할 관점은 (a) 큐의 경로(로컬인지 원격인지, 프라이빗인지 퍼블릭인지), (b) 트랜잭션 큐인지, 메시지를 Recoverable로 하고 있는지(둘 다 아니라면 현행 시스템은 재시작 시 메시지가 소실되는 것을 전제로 동작하고 있음), (c) 포맷터(XmlMessageFormatter / BinaryMessageFormatter / ActiveXMessageFormatter), (d) 저널·배달 불가 큐의 사용, (e) 누가 생성·삭제하는가(인스톨러인지 앱인지)입니다.재고 조사는 확인만 해서는 이전 판단에 효력이 없습니다. 다음 형태로 큐 1개당 1행을 채워, 그대로 이전 방침의 근거로 삼으십시오(그대로 결재 자료의 별지로 사용할 수 있습니다).
관점 어디를 볼 것인가 기록할 내용 판정에 미치는 영향 (a) 큐의 경로 MessageQueue에 넘기는 경로 문자열, 구성 파일의 접속처,FormatName:지정로컬/원격 구분, 프라이빗/퍼블릭 구분, 상대 머신명 원격·거점 간이라면 store-and-forward의 필요 여부를 판정(5.2의 「오프라인 내성」 행). 로컬 완결이라면 5.2의 첫 번째 행에 해당 (b) 트랜잭션과 Recoverable 큐 생성 지점(트랜잭션 큐인지), 송신 시 Recoverable을 지정하고 있는지, DTC를 사용하고 있는지 트랜잭션 큐 여부, Recoverable 지정 유무, DTC 참여 유무 DTC가 있다면 이전 대상은 테이블 큐로 거의 확정. 둘 다 없는 경우, 현행이 「재시작 시 메시지가 사라지는 것을 전제」로 운용되고 있음을 명기한다 (c) 포맷터 XmlMessageFormatter/BinaryMessageFormatter/ActiveXMessageFormatter지정 지점사용 중인 포맷터와 메시지 본문의 타입 BinaryFormatter 계열이라면 JSON으로의 교체가 필수(절차 2). 이전 공수의 주요 원인이 된다7 (d) 저널·배달 불가 큐 큐 속성(저널 활성화 여부), 시스템 큐 내용, 운용 절차서 저널 이용 여부, 배달 불가 큐를 실제로 확인하고 있는지 「실패한 메시지를 어떻게 다루는가」가 이전 대상의 설계 요건이 된다(테이블 큐라면 대피 테이블) (e) 누가 생성·삭제하는가 인스톨러, 배포 스크립트, 앱 시작 시의 Create호출생성 주체와 권한 설정을 누가 하는가 이전 시 「새 큐를 누가 준비하는가」가 그대로 대체된다. 동결을 선택하는 경우에는 키팅 절차에 옮겨 적는다(7장) - 메시지 형식을 정한다. 이전 후의 형식은 JSON을 기본으로 합니다. 가져오면 안 되는 것은 BinaryMessageFormatter와 같은 BinaryFormatter 계열의 직렬화입니다. BinaryFormatter는 .NET 9에서 이미 삭제되었으며, 보안상으로도 권장되지 않습니다.7 한편 Protocol Buffers나 MessagePack처럼 사양이 독립적으로 유지보수되는 바이너리 형식을 사용하고 있다면, 그 형식을 새 시스템으로 그대로 옮기는 것 자체는 문제없습니다.
- 수신 측부터 이전한다. 새로운 큐(예를 들어 테이블 큐)를 먼저 준비하고, 수신 측 처리를 새 큐에 대응시킨 다음, 송신 측을 전환합니다. 이전 기간 중에는 옛 MSMQ에서 새 큐로 메시지를 옮기는 작은 브리지(이것은 .NET Framework 그대로여도 됩니다)를 끼워 넣으면, 송신 측을 한꺼번에 전환하지 않아도 됩니다. 다만 브리지는 「MSMQ에서의 꺼내기」와 「새 큐로의 쓰기」 사이에서 죽으면 메시지를 잃거나 중복으로 흘려보냅니다. 이전 원본이 트랜잭션 큐라면, MSMQ 쪽을 트랜잭션 수신으로 만들어 쓰기 실패 시 되돌릴 수 있도록 하고, 새 큐 쪽은 메시지 ID로 중복을 걸러낼 수 있는(멱등한) 설계로 하는 것이 최소 조건입니다. 이전 원본이 비트랜잭션 큐인 경우에는 이 방법을 쓸 수 없습니다. 큐의 트랜잭션 속성은 생성 시에 결정되며 나중에 바꿀 수 없기 때문입니다. 그 경우에는 「Peek으로 읽는다 → 새 큐에 멱등하게 쓴다 → 쓰기가 완료된 것을 확인한 뒤 제거한다」의 순서로 구성합니다(제거하기 전에 죽더라도, 중복은 새 큐 쪽의 멱등성이 흡수합니다). 어느 쪽의 구현도 규모에 맞지 않는다면, 브리지를 끼우지 않고 절차 5의 「비운 뒤 전환」으로 밀어붙이는 편이 안전합니다.
- 동작을 고정한 뒤 다시 작성한다. 큐 처리는 타이밍에 의존하는 버그가 나오기 쉬운 영역입니다. 이전 전에 「이 입력 메시지라면 이 결과」라는 특성화 테스트를 준비해 두면, 교체 후 검증이 기계적으로 이루어집니다(「특성화 테스트로 동작을 고정한 뒤 리팩터링하기」).
- 큐 내용이 빈 상태에서 전환한다. 메시지가 남은 상태에서의 전환은 이중 처리·누락의 온상입니다. 계획 정지로 큐를 완전히 비운 뒤 전환하는 것이 결국 가장 안전하고 빠른 방법입니다.
7. 동결을 선택하는 경우의 최소 조건
「앱을 .NET으로 올릴 계획이 없고, 비용 대비 효과 면에서도 당분간 이전하지 않는다」는 판단은 조건부로 합리적입니다. 그 경우의 최소 조건을, 결재·리뷰 자료에 그대로 옮겨 적을 수 있는 체크리스트 형태로 제시합니다. 4가지 모두에 표시가 되어야 비로소 「동결」이 선택지가 된다고 생각해 주십시오.
| 확인 | 최소 조건 | 구체적으로 할 일 | 충족하지 못하면 무슨 일이 일어나는가 |
|---|---|---|---|
| □ | 키팅·복원 절차에 MSMQ를 명기한다 | MSMQ는 Windows의 옵션 기능이다. 「Windows 기능 켜기/끄기」 또는 DISM/PowerShell을 통한 활성화 절차를 환경 구축 절차서에 적어 둔다 | PC·서버 교체 시 활성화를 잊어, 이전 당일 원인 불명으로 동작하지 않는다 |
| □ | 큐 길이를 감시한다 | 체류 건수의 임계값 감시와 알림을 심어 둔다. 배달 불가 큐와 저널 큐도 대상에 포함한다 | 수신 측이 멈춰도 오류가 되지 않고 계속 쌓이기 때문에, 아무도 눈치채지 못한 채 업무가 멈춘다 |
| □ | 구성을 문서화한다 | 큐의 경로, 권한, 트랜잭션 유무, 포맷터, 저널 설정을 기록한다(6장의 재고 조사표를 그대로 사용할 수 있다) | 향후 이전 견적이 조사부터 다시 시작하게 되어, 정확도와 공수 모두 악화된다 |
| □ | 연 1회, 판단을 재검토한다 | 「비권장 목록에 올랐는가」「OS 업데이트로 동작이 바뀌지 않았는가」를 연 단위로 확인한다 | 비권장 공지가 나온 뒤에야 허둥지둥 움직이게 된다 |
네 번째는 특히 가볍게 여겨지기 쉽지만, 이 연간 확인이 있기 때문에 공지가 나온 뒤에도 당황하지 않을 수 있습니다. 반대로 이 4가지가 채워지지 않는 환경에서의 동결은 「보이지 않는 채로 멈추기를 기다리는」 것과 같습니다.
8. 정리
- MSMQ는 2026년 7월 시점에 공식적으로 비권장 상태가 아니며, OS 기능으로서는 존속하고 있습니다. 「이미 폐지됨」이라는 인식은 정확하지 않습니다.12
- 한편 System.Messaging은 .NET Framework 전용인 채로 멈춰 있고, 호환 팩에도 포함되지 않으며, .NET(Core 이후)으로의 공식적인 길은 없습니다. CoreWCF 자체에는 지원 정책이 있지만, 그 MSMQ 트랜스포트는 System.Messaging의 커뮤니티 이식에 의존합니다.345
- 판단의 축은 「앱을 .NET으로 올릴 것인가」입니다. 올린다면 큐도 함께 이전하는 것이 원칙이며(P/Invoke 등을 통한 연명은 유지보수 부담과의 맞교환), 동결이라면 감시와 문서화를 조건으로 성립합니다.6
- 이전 대상의 제1후보는 DB의 테이블 큐입니다. MSMQ+DTC의 정합성은 로컬 트랜잭션으로 대체하는 것이 본질이며, 브로커 제품의 도입은 그 이후에 검토합니다.
- BinaryFormatter 계열의 직렬화는 .NET 9에서 삭제되어 새 시스템으로 가져올 수 없습니다. 이전 시에는 JSON을 기본으로 하되, protobuf 등 독립적으로 유지보수되는 형식이라면 그대로 사용해도 무방합니다.7
- 수신 측 → 브리지 → 송신 측의 순서로 전환하고, 특성화 테스트로 동작을 고정한 뒤 다시 작성하는 것이 사고 없이 진행하는 방법입니다.
관련 글
- VB6 애플리케이션은 언제까지 동작할까 ── 런타임 지원 현황과 현실적인 .NET 이전 진행 방법
- .NET Framework를 .NET으로 이행하기 전에 확인해야 할 것 - 착수 전에 승부가 결정되는 실천 체크리스트
- 테스트 없는 레거시 업무 애플리케이션에 안전하게 손을 대는 방법 ── 특성화 테스트와 리팩터링의 실천
- Windows의 프로세스 간 통신을 어떻게 선택할까 ── 네임드 파이프 / TCP / gRPC / 공유 메모리 / COM 판단표
- TCP에서 Send한 단위대로 Receive할 수 있다는 오해 ── 바이트 스트림으로 다루기 위한 수신 설계
- 소스 코드도 사양서도 없는 시스템을 인계받았다면 ── 멈추지 않고 운용·보수하기 위한 실무 절차
- 업무 앱의 DB 스키마를 버전 관리하는 법 ── 「거래처마다 DB가 다르다」를 막는 마이그레이션 실천
관련 상담 영역
합동회사 코무라소프트에서는 MSMQ를 포함한 레거시 구성의 재고 조사와 이전 계획, .NET Framework에서 .NET으로의 이전, 큐 처리를 포함한 업무 시스템 개수를 다루고 있습니다.
참고 링크
-
Microsoft Learn, Deprecated features in the Windows client. Windows 클라이언트에서 적극적인 개발이 종료된(비권장) 기능의 공식 목록. 2026년 7월 시점의 목록에 NTLM, VBScript, WordPad 등은 실려 있는 반면 MSMQ(Microsoft Message Queuing)는 실려 있지 않다는 점, 그리고 비권장(deprecated)이 「적극적으로 개발되고 있지 않아 향후 업데이트에서 제거될 가능성이 있는」 단계이며 삭제(removed)와는 구별된다는 점에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Features Removed or No Longer Developed in Windows Server. Windows Server에서 삭제된 기능과 개발 종료(비권장) 기능의 공식 목록. Windows Server 2025 탭을 포함한 2026년 7월 시점의 목록에 MSMQ가 실려 있지 않다는 점, 비권장 구성 요소도 Windows Server에 계속 동봉되어 제품 라이프사이클에 따라 프로덕션 환경용으로 지원되며, 보안 업데이트와 품질 업데이트를 계속 받는다는 점에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, MessageQueue Class (System.Messaging). System.Messaging.MessageQueue 클래스의 레퍼런스가 대상으로 하는 버전이 .NET Framework 1.1부터 4.8.1까지이며, .NET(Core 이후)용 버전은 존재하지 않는다는 점에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Use the Windows Compatibility Pack to port code to .NET. Windows 호환 팩(Microsoft.Windows.Compatibility 패키지)이 .NET Framework 전용 API에 대한 의존을 .NET 이전 시에 보완하기 위해 약 2만 개의 API를 제공한다는 점, 그 기술 영역 목록(CodeDom, 구성, Directory Services, Drawing, ODBC, ACL, WCF, 레지스트리, WMI, 성능 카운터, Windows 서비스, EventLog 등)에 메시징(System.Messaging)이 포함되어 있지 않다는 점에 대해. ↩ ↩2 ↩3 ↩4
-
CoreWCF project, CoreWCF.MSMQ (NuGet) 및 CoreWCF 1.4.0 Preview release, Microsoft, CoreWCF Support Policy. CoreWCF가 WCF의 서버 측을 .NET으로 이식하는 커뮤니티 주도 프로젝트이며 마이크로소프트가 공식 지원 정책을 제공한다는 점, 큐 계열 트랜스포트의 일환으로 MSMQ 대응(CoreWCF.MSMQ 패키지)이 공개되어 있다는 점, 그 MSMQ 구현이 .NET Framework판 System.Messaging 라이브러리의 커뮤니티 이식에 의존하고 있다는 점에 대해. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, .NET Framework official support policy. .NET Framework 4.8이 Windows 운영체제의 구성 요소로 정의되며, 설치되어 있는 상위 제품(OS)의 라이프사이클 정책에 따라 지원된다는 점에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, BinaryFormatter migration guide. BinaryFormatter가 보안상의 이유로 단계적으로 폐지되었으며, .NET 9 이후에는 런타임에서 구현이 삭제되어 기본값으로는 사용할 수 없다는 점, 그리고 이전 대상으로 JSON(System.Text.Json) 등의 안전한 직렬화 형식이 안내되고 있다는 점에 대해. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn(아카이브), Express and Recoverable Messaging, System-Generated Queues, Message Queuing (MSMQ). express 메시지가 전송 중에도 전송 후에도 RAM상에 보관되며, 메시지가 존재하는 컴퓨터의 정지나 MSMQ 서비스의 정지로 소실된다는 점, recoverable 메시지가 송신 측과 중계하는 각 컴퓨터에서 디스크에 기록되고 대상 큐에서도 디스크상에 보관된다는 점, 퍼블릭 큐가 디렉터리 서비스에 등록되는 한편 프라이빗 큐는 로컬 컴퓨터에만 등록되어 디렉터리 서비스에는 공개되지 않는다는 점, 큐에서 꺼낸 메시지나 송신 완료 메시지의 사본을 보관하는 저널 큐와 전달하지 못한 메시지를 보관하는 배달 불가(데드레터) 큐가 MSMQ가 생성하는 시스템 큐라는 점에 대해. ↩
-
Microsoft Learn(아카이브), Message Queuing Functions 및 MQSendMessage, MQReceiveMessage. MSMQ의 Win32 네이티브 API(MQCreateQueue, MQSendMessage, MQReceiveMessage 등)가 C/C++ 애플리케이션용으로 문서화되어 있어, 큐의 생성·송신·수신을 관리 API를 거치지 않고 수행할 수 있다는 점에 대해. ↩
-
Microsoft Learn, 테이블 힌트 (Transact-SQL).
READPAST가 다른 트랜잭션이 잠그고 있는 행을 읽지 않고 건너뛰는 힌트이며, 건너뛸 수 있는 것은 행 수준 잠금뿐으로 페이지 수준 잠금은 건너뛸 수 없다는 점,READ COMMITTED또는REPEATABLE READ격리 수준에서만 지정할 수 있다는 점에 대해. 특히 「READ_COMMITTED_SNAPSHOT데이터베이스 옵션이ON으로 설정되어 있고, 또한 (a) 세션의 트랜잭션 격리 수준이READ COMMITTED이거나, (b) 쿼리에READCOMMITTED테이블 힌트도 지정되어 있는 것 중 하나가 참인 경우,READPAST테이블 힌트는 지정할 수 없다. 이 경우에READPAST힌트를 지정하려면,READCOMMITTED테이블 힌트가 있다면 삭제하고 쿼리에READCOMMITTEDLOCK테이블 힌트를 포함시킨다」는 서술에 대해.READCOMMITTEDLOCK이READ_COMMITTED_SNAPSHOT설정과 무관하게 잠금에 의한READ COMMITTED를 수행시키는 힌트라는 점, 그리고 하나의 테이블에 대해 세분성 힌트(PAGLOCK/NOLOCK/READCOMMITTEDLOCK/ROWLOCK/TABLOCK/TABLOCKX)를 2개 이상 지정할 수 없다는 점도 같은 페이지를 참조. 데이터베이스 쪽 설정은 sys.databases 의is_read_committed_snapshot_on으로 확인할 수 있습니다. ↩ ↩2 -
Microsoft Learn, TOP (Transact-SQL). INSERT·UPDATE·MERGE·DELETE에서 TOP을 사용하는 경우, 참조되는 행이 어떤 순서로 정렬되는 것은 아니라는 점, 그리고 순서를 정하고 싶을 때는 TOP과 ORDER BY를 가진 하위 쿼리를 사용한다는 점에 대해. ↩
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
멀티스레드 실무 베스트 프랙티스 .NET 편 ── 스레드를 늘리기 전에 정해둘 것
「스레드를 만들면 가끔 죽거나 멈춘다」를 막는 설계의 정석을 .NET/C# 대상으로 정리합니다. 스레드를 직접 만들지 않고 Task에 올라타기, 공유 가변 상태 줄이기, 락의 규율, CancellationToken을 통한 정지 설계, UI 스레드...
WMI/CIM을 C#・PowerShell에서 사용하기 ── 하드웨어 정보 취득・프로세스 감시・원격 조회 실무 가이드
PC 시리얼 번호 취득, 디스크 여유 공간 감시, 프로세스 시작 감지의 정석이 WMI/CIM입니다. Get-CimInstance 등 CIM 계열 명령어의 사용법과 구형 Get-WmiObject로부터의 이전, C#의 System.Management...
DLL・COM 인터페이스의 하위 호환성 ── 어떤 변경이 호출 측을 망가뜨리는지의 판단표
DLL이나 COM 컴포넌트의 어떤 변경이 호출 측을 망가뜨리는지. 바이너리 호환・소스 호환・동작 호환의 3계층을 정리하고, 변경 내용별 판단표, COM 인터페이스 불변의 철칙, semver 운용까지를 실무 가이드로 정리합니다.
테스트 없는 레거시 업무 애플리케이션에 안전하게 손을 대는 방법 ── 특성화 테스트와 리팩터링의 실천
테스트가 없는 업무 애플리케이션에 안전하게 손을 대기 위해, 현재 동작을 고정하는 특성화 테스트(골든 마스터 기법)의 절차, 이음매(seam)를 만드는 방법, 리팩터링과 기능 추가를 섞지 않는 운영 규칙을 C# 예제로 설명합니다.
VB6 애플리케이션은 언제까지 동작할까 ── 런타임 지원 현황과 현실적인 .NET 이전 진행 방법
VB6 애플리케이션은 언제까지 동작할까요. VB6 런타임의 지원 정책(Windows 11도 지원 대상)과 IDE 지원은 이미 종료되었다는 비대칭적인 현황을 정리하고, 전면 재작성・자동 변환・단계적 이전의 판단표, 이전 전 자산 파악, VB6와 ....
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
ActiveX 이관
COM / ActiveX / OCX 자산을 유지할지, 감쌀지, 교체할지의 단계적 판단을 정리한 토픽 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
기존 자산 활용 & 이관 지원
COM / ActiveX / OCX 자산, 네이티브 코드, 32비트 의존성을 유지하면서 단계적인 이관 계획을 지원합니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- MSMQ는 폐지되었나요?
- 아니요. 2026년 7월 시점에 Windows 클라이언트의 비권장 기능 목록에도, Windows Server의 「삭제된 기능·개발 종료 기능」 목록에도 MSMQ는 실려 있지 않습니다. Windows의 옵션 기능으로서 현행 OS에도 동봉되어 있습니다. 「폐지되었다」는 표현이 퍼진 것은 .NET 쪽 상황과 혼동되고 있기 때문입니다. MSMQ를 다루는 표준 클래스 라이브러리 System.Messaging은 .NET Framework에만 존재하며, .NET(Core 이후)으로는 이식되지 않았습니다. 즉 정확히 말하면 「OS 기능으로서는 존속하고 있지만, 모던 .NET에서의 공식적인 이용 수단이 없는」 상태입니다.
- .NET 8나 .NET 10에서 MSMQ를 사용할 방법은 없나요?
- 공식적인 관리 API는 없습니다. System.Messaging은 .NET Framework 4.8.1까지의 API이며, Windows 호환 팩(Microsoft.Windows.Compatibility)에도 포함되어 있지 않습니다. MSMQ의 네이티브 Win32 API(MQSendMessage 등)를 P/Invoke로 호출하는 것은 기술적으로는 가능하지만, 포맷터나 트랜잭션 연계를 포함한 래퍼를 직접 만들고 유지보수하는 일을 떠안게 됩니다. 커뮤니티 쪽에서는 CoreWCF 프로젝트가 MSMQ 트랜스포트용 패키지(CoreWCF.MSMQ)를 공개하고 있지만, 이는 큐를 경유해 호출되는 WCF 서비스를 모던 .NET에서 호스팅하기 위한 것(WCF 서버 측의 이식)이며, System.Messaging을 대신할 범용 큐 API도, 송신 측 클라이언트의 대체도 아닙니다. 구현 또한 .NET Framework판 System.Messaging의 커뮤니티 이식에 의존하고 있습니다. 검증이나 잠정적인 연명의 선택지는 될 수 있습니다. CoreWCF 자체에는 마이크로소프트의 공식 지원 정책이 있지만, 이 MSMQ 구현이 의존하는 System.Messaging의 커뮤니티 이식까지 같은 보증이 있는 것은 아니므로, 업무 시스템의 전제로 삼는다면 사용할 버전이 지원 정책의 대상인지와 의존 부분의 취급을 확인한 뒤 판단해야 합니다. 실무적으로는 앱을 .NET으로 올리는 시점에 큐도 함께 이전하는 것이 본래의 방향입니다.
- 이전 대상은 RabbitMQ와 Azure Service Bus 중 어느 쪽이 좋을까요?
- 그 둘 중 하나를 고르기 전에, 데이터베이스 테이블을 큐로 만드는 방안을 검토하십시오. MSMQ를 사용하는 중소 규모 업무 시스템 대부분은 큐의 상대가 자사 데이터베이스를 갱신하는 처리입니다. 이 경우 테이블 큐라면 업무 데이터와 같은 로컬 트랜잭션으로 「메시지 꺼내기」와 「업무 데이터 갱신」을 확정할 수 있어, MSMQ+분산 트랜잭션(DTC)으로 구현하고 있던 정합성을 더 단순한 구조로 대체할 수 있습니다. 테이블 큐로는 충족되지 않는 요건, 예를 들어 복수 시스템 간의 느슨한 결합 배포나 높은 처리량이 필요한 경우에, 온프레미스 요건이 강하면 RabbitMQ, 클라우드에 둘 수 있다면 Azure Service Bus를 검토하는 순서가 실패하기 어렵습니다.
- 당분간 .NET Framework 상태 그대로 동결하는 판단도 괜찮을까요?
- 조건부로 괜찮습니다. .NET Framework 4.8은 Windows의 구성 요소로서 OS의 라이프사이클에 따라 지원되고, MSMQ 자체도 비권장 상태가 아니므로 「계속 동작한다」는 전망은 세울 수 있습니다. 다만 동결을 선택하는 경우에는, 키팅 절차에 MSMQ 기능의 활성화를 명기하고, 큐 길이와 저널의 감시를 심어 두고, 메시지 형식과 접속 구성을 문서화하고, 담당자 이동에 대비해 복구 절차를 남겨 두는 4가지를 최소한 실행하십시오. 동결의 진짜 위험은 기술이 아니라 「손댈 수 있는 사람이 없어지는」 것입니다.