수정 이력(9건, 최종 수정 2026년 09월 03일)
이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.
- Codex 리뷰에 따라 상담·문의 링크에 /ko/ 로케일 접두를 붙였습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
- permalink·저자 표기·지식 맵 래퍼·깨진 내부 링크 등 CI가 지적한 표시용 수정을 반영했습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
- 이 기사의 지식 맵을 다시 검토해, 본문이 말하는 범위보다 넓게 읽히던 관계를 고쳤습니다. 본문의 주장은 바꾸지 않았습니다.
- 기사 맨 앞에 「이 기사의 지식 맵」을 추가했습니다. 본문에서 다루는 개념 사이의 관계를 한 장의 그림으로 볼 수 있습니다. 관계 전체 목록(근거 URL·확신도·확인일 포함)과 주요 개념 정의는 지식 맵 상세 페이지에 모았고, 기계 가독 데이터를 JSON-LD와 Turtle로 공개합니다.
- 테이블 큐의 꺼내기 SQL에 `READCOMMITTEDLOCK`을 넣었습니다. 공식 문서는 「`READ_COMMITTED_SNAPSHOT`이 `ON`이고 세션의 isolation level이 `READ COMMITTED`일 때 `READPAST` 테이블 힌트는 지정할 수 없다」고 명시하며, 그 경우의 대처로 `READCOMMITTEDLOCK`을 함께 적는 방법을 듭니다. 기본 isolation level은 `READ COMMITTED`이므로, `READ_COMMITTED_SNAPSHOT`만 켠 데이터베이스(Azure SQL Database에서는 기본이 ON)에서는 이 꺼내기가 「다른 worker의 행을 건너뛴다」는 수준이 아니라 문 자체가 오류가 되고 있었습니다. 흔히 넣는 `ROWLOCK`을 뺀 이유는 `ROWLOCK`과 `READCOMMITTEDLOCK`이 같은 단위(granularity) 힌트 그룹이라 한 테이블에 둘 다 지정할 수 없기 때문입니다. 설정 확인 방법과, `OFF`임이 분명할 때의 선택도 본문에 보탰습니다.
- 테이블 큐의 `ORDER BY Id`가 「넣은 순서대로 처리된다」는 보장으로 읽히던 서술을 고쳤습니다. 맞춰지는 것은 꺼내는 순서뿐이며, `READPAST`로 병렬 처리하는 이상 먼저 꺼낸 job이 나중에 commit될 수 있습니다. 반영 순서에 의미가 있는 연동에서는 소비자를 하나로 두거나 키 단위로 나눠야 한다는 점, 둘 다 하지 않으면 전체 FIFO는 없다고 명시해야 한다는 점을 보탰습니다.
- 테이블 큐의 꺼내기 SQL에 정렬 지정이 없어, 넣은 순서대로 꺼내진다는 보장이 없었습니다. SQL Server 사양상 TOP은 대상 행을 정렬하지 않으므로 오래된 job이 계속 뒤로 밀립니다. 정렬을 지정하는 형태로 고쳤습니다.
- 판단표가 전제로 하는 MSMQ 용어를 2장 끝에 최소 세트로 정의했습니다. 더불어 지원 현황을 OS 기능·Win32 API·.NET Framework·.NET·CoreWCF 계층별로 표로 정리하고, 1순위 후보로 권하는 테이블 큐의 최소 구현, 현황 조사 관점의 기록용 대응표, 현상 유지 조건 체크리스트를 추가했습니다.
- 본문 속 관련 기사 링크 문구가 링크 대상의 현재 제목과 어긋나 있던 부분을 실제 제목에 맞췄습니다. 본문 내용은 바꾸지 않았습니다.
- 최초 공개
이 글을 인용하기(DOI(등록된 아카이브): 10.5281/zenodo.22175284)
아래 DOI는 이전에 등록된 아카이브를 가리키며 현재 본문과 다를 수 있습니다. 현재 본문을 참조할 때는 이 페이지의 URL을 사용하세요.
Go Komura (2026). 「MSMQ는 언제까지 쓸 수 있는가 ── 「deprecated조차 아닌」 레거시 큐의 마이그레이션 판단」. 합동회사 코무라소프트. https://comcomponent.com/ko/blog/msmq-migration-decision-guide/
- DOI(등록된 아카이브)
- 10.5281/zenodo.22175284
- DOI(마지막 등록 버전)
- 10.5281/zenodo.22175285
「이 시스템이 MSMQ를 쓰는데요, 폐지되었다고 들었습니다. 바로 옮기지 않으면 곤란합니까」──레거시 마이그레이션 상담에서 최근 1년 정도 반복해서 듣는 질문입니다.
답은 조금 꼬여 있습니다. MSMQ(Microsoft Message Queuing)는 폐지는커녕 공식으로는 deprecated조차 아닙니다. 2026년 7월 시점에서 Microsoft의 deprecated 기능 목록에 MSMQ 이름은 없고, 현행 Windows에도 선택 기능으로 들어 있습니다. 그런데도 현장 감각으로 「끝난 기술」인 이유는 .NET 쪽의 공식 managed API가 닫혀 있기 때문입니다. MSMQ를 다루는 표준 라이브러리 System.Messaging은 .NET Framework에만 있고, .NET(Core 이후)에는 이식되지 않았습니다.
즉 MSMQ는 「내일 멈춘다」는 기술이 아니라, 「앱을 .NET으로 올리려는 순간에 벽이 되는」 기술입니다.
이 기사의 대상 독자는 MSMQ를 쓰는 기존 시스템의 유지보수·마이그레이션을 맡은 개발자와, 그 판단을 요구받는 정보시스템 부서입니다. 「폐지되었다더라」는 소문의 진위를 확인하고, 옮길지를 자기 환경 조건으로 정할 수 있는 상태를 목표로 합니다.
이 기사에서는 소문과 사실을 가른 뒤, 계속 쓸지·옮길지의 판단과 이전 대상 고르는 법을 정리합니다. VB6나 .NET Framework에서의 마이그레이션 자체는 「VB6에서 .NET으로의 마이그레이션 실무」「.NET Framework→.NET 마이그레이션 전 체크리스트」에서 다루므로, 본 기사는 큐 부분에 한정합니다.
1. 먼저 결론
- MSMQ는 공식으로는 deprecated가 아닙니다. Windows 클라이언트의 deprecated 기능 목록에도, Windows Server의 제거·개발 종료 기능 목록에도 없습니다(2026년 7월 시점).12
- 그러나 .NET의 공식 managed API는 없습니다. System.Messaging이 대상으로 하는 것은 .NET Framework 1.1〜4.8.1뿐이며,3 .NET 마이그레이션 때의 호환 수단인 Windows 호환 팩에도 들어 있지 않습니다.4 native Win32 API를 P/Invoke로 호출하는 길은 남아 있으나, 수명 연장 수단의 위치입니다(3장).
- CoreWCF에 의한 이식은 있으나, 대상은 큐를 통한 WCF 서비스(수신 측) 이전에 한정되며, 조건을 확인한 뒤 쓰는 경로입니다. CoreWCF 자체에는 Microsoft의 지원 정책이 있는 한편, MSMQ transport(CoreWCF.MSMQ)의 구현은 .NET Framework판 System.Messaging의 커뮤니티 이식에 의존합니다.5
- 판단의 축은 「앱을 .NET으로 올릴지」입니다. 올린다면 큐도 함께 옮기는 것이 원칙입니다(P/Invoke나 CoreWCF로 수명을 연장하는 길도 있으나, 유지보수 부담을 떠안는 판단이 됩니다). 올리지 않으면(현상 유지) .NET Framework 4.8이 OS 구성 요소로 지원되는 동안은 계속 돌리는 계획을 세울 수 있습니다.6
- 이전 대상의 1순위 후보는 메시지 브로커가 아니라 데이터베이스의 테이블 큐입니다. MSMQ+분산 트랜잭션(DTC)으로 지키던 정합성은, 업무 DB와 같은 트랜잭션으로 처리할 수 있는 테이블 큐가 더 단순하게 대체합니다(5장).
- 메시지 형식 현황 조사를 먼저 하십시오. BinaryFormatter 계열의 serialization은 .NET 9에서 런타임에서 삭제되었고,7 옛 형식 그대로 신구를 병행하면 막힙니다(6장).
그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 28건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle
2. MSMQ를 30초 만에
MSMQ는 Windows에 포함되어 온 메시지 큐 기반입니다. 애플리케이션은 큐에 메시지를 쓰고, 다른 애플리케이션(같은 머신이든 다른 머신이든)이 편한 타이밍에 꺼냅니다. 특징은 다음 세 가지로 모입니다.
- store-and-forward: 송신처가 내려가 있어도 메시지는 로컬에 쌓였다가 회복 후 도착합니다. 거점 사이 불안정한 회선에 강합니다. 다만 이 영속성은 무조건이 아니며, 기본의 고속(express) 메시지는 메모리에 남을 수 있어 MSMQ 서비스나 머신 재시작으로 잃습니다. 디스크에 영속화되는 것은 송신 측에서 Recoverable(회복 가능)을 지정한 메시지이거나 트랜잭션 메시지입니다.
- 트랜잭션: 큐 조작을 트랜잭션으로 둘 수 있고, MS DTC(분산 트랜잭션 코디네이터)와 조합하면 「큐에서 꺼내기」와 「데이터베이스 갱신」을 하나의 분산 트랜잭션으로 확정할 수 있습니다.
- OS 포함: 추가 미들웨어 도입 없이 쓸 수 있어, 2000년대 업무 시스템, 특히 수발주 데이터 연동, 전표·리포트의 비동기 처리, 제조 현장의 공정 간 연동 등에서 널리 채택되었습니다.
이 「OS에 들어 있고, 트랜잭션이 되며, 오프라인에 강하다」는 조합이 뛰어났기 때문에 지금도 현역으로 남아 있습니다. 이전 대상을 생각할 때도, 이 세 가지 중 무엇을 실제로 쓰고 있는지가 판단의 중심이 됩니다.
본 기사에서 쓰는 용어의 최소 세트
5장의 판단표와 6장의 현황 조사는 다음 용어를 전제로 합니다. 여기서 한 번에 잡아 두십시오.8
- private queue ── 디렉터리 서비스(Active Directory)에는 게시하지 않고, 그 컴퓨터에만 등록되는 큐입니다.
.\private$\큐이름같은 형식으로 지정합니다. 상대인 public queue는 디렉터리 서비스에 등록되어 도메인 안에서 검색할 수 있습니다. 중소 규모 업무 시스템에서 쓰이는 것은 거의가 private queue입니다. - express 메시지 ── 기본 송신 모드입니다. 배송 중에도 배송 후에도 메모리에 두므로 빠르지만, MSMQ 서비스나 머신이 멈추면 잃습니다.
- recoverable(회복 가능) 메시지 ── 송신 측과 중계하는 각 컴퓨터에서 디스크에 쓰이고, 목적지 큐에서도 디스크에 유지되는 모드입니다. 재시작을 넘어 남습니다. 송신 측이 명시적으로 지정합니다.
- 트랜잭션 큐 ── 트랜잭션 메시지만 다루는 큐입니다. 큐 만들 때 정해지고, 나중에 바꿀 수 없습니다(6장의 이전 절차에 영향을 줍니다).
- DTC(분산 트랜잭션 코디네이터) ── 여러 리소스(MSMQ 큐와 데이터베이스 등)에 걸친 트랜잭션을 조정하는 Windows 서비스입니다. 「큐에서 꺼내기」와 「DB 갱신」을 한 덩어리로 확정하는 구조이며, 5장의 이전 판단에서 가장 큰 쟁점이 됩니다.
- journal queue / 배달 불능(dead-letter) queue ── MSMQ가 자동 생성하는 시스템 큐입니다. journal은 「보낸/꺼낸 메시지의 사본」을, dead-letter queue는 「배달되지 못한 메시지」를 쌓습니다. 방치하면 용량을 먹으므로 현상 유지 운영에서는 감시 대상이 됩니다(7장).
3. 사실 관계 ── 「폐지되었다」는 정확하지 않다
「MSMQ는 쓸 수 있는가」라는 질문이 혼란스러운 이유는, 계층마다 답이 다른데 한 덩어리로 말하기 때문입니다. 먼저 전체 모습을 표 한 장으로 둡니다.
| 계층 | 현재 상태 | 실무상의 의미 |
|---|---|---|
| OS 기능(Windows의 선택 기능) | 존속. 현행 Windows 클라이언트/Server에 포함되며, deprecated 선언도 나오지 않음12 | 활성화하면 지금도 동작합니다. OS 지원 기간 안에서는 계속 쓸 수 있습니다 |
Win32 native API(MQSendMessage 등) |
문서화되어 있으며 이용 가능9 | P/Invoke로 .NET에서 호출하는 길은 남습니다. 다만 formatter와 트랜잭션 연동은 직접 써서 유지보수하게 됩니다 |
| .NET Framework의 System.Messaging | 이용 가능. 다만 대상은 .NET Framework 1.1〜4.8.13 | 기존 시스템은 여기서 돌아가고 있습니다. 이 앞이 없습니다 |
| .NET(Core 이후)의 공식 managed API | 존재하지 않음. Windows 호환 팩에도 포함되지 않음4 | 앱을 .NET으로 올리는 순간에 큐 부분을 다시 만들어야 합니다 |
| WCF의 MSMQ 바인딩 → CoreWCF.MSMQ | 커뮤니티 주도 이식이 존재. CoreWCF 자체에는 지원 정책이 있으나, MSMQ 구현은 System.Messaging의 커뮤니티 이식에 의존5 | 큐를 통해 호출되는 WCF 서비스(수신 측) 수명 연장에는 쓸 수 있으나, 송신 측 대체도 범용 큐 API도 아닙니다 |
| 신규 채택 | 권할 수 없음 | 공식적인 앞길이 없기 때문입니다 |
이하, 이 표의 각 행 근거를 순서대로 확인합니다.
첫째, MSMQ는 deprecated 목록에 없습니다. 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 정확히 말하면 닫힌 것은 managed API입니다. MSMQ의 Win32 native API(MQSendMessage, MQReceiveMessage 등)는 지금도 문서화되어 있어, .NET에서 P/Invoke로 호출하는 것 자체는 가능합니다. 다만 System.Messaging이 흡수하던 formatter와 트랜잭션 연동을 자체 wrapper로 쓰고 계속 유지보수하게 되므로, 「계속 쓰기 위한 본래 선택」이 아니라 「어쩔 수 없이 남길 때의 수명 연장 수단」의 위치입니다.
셋째, WCF의 MSMQ 연동도 같은 벽 안에 있습니다. .NET Framework의 WCF에는 MSMQ를 하위로 쓰는 바인딩이 있었으나, 이 경로를 현대 .NET에서 재현하려면 커뮤니티 주도의 CoreWCF에 이릅니다. CoreWCF는 큐 계열 transport의 일환으로 MSMQ 대응 패키지(CoreWCF.MSMQ)를 공개하지만, 그 구현은 .NET Framework판 System.Messaging 라이브러리의 커뮤니티 이식에 대한 의존을 명시합니다.5 돌아가는지로 말하면 돌아가고, CoreWCF 자체에는 Microsoft의 공식 지원 정책이 있으므로 「완전히 비공식 라이브러리」도 아닙니다.5 다만 MSMQ transport가 의존하는 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 갱신을 atomic하게」라는 설계는 클라우드 계열 큐 서비스에서는 재현할 수 없습니다. 여기를 방치한 채 주변만 .NET화하면 마지막에 가장 단단한 심이 남습니다.
- serialization 형식의 시한폭탄. 옛 시스템의 메시지 본문은 binary serialization인 경우가 있고, BinaryFormatter는 .NET 9에서 런타임에서 삭제되었습니다.7 이전 때 메시지 형식까지 다시 봐야 합니다.
즉 「MSMQ는 언제까지 쓸 수 있는가」라는 질문의 실무적 답은 「OS가 지원하는 한 동작하지만, 이전 난이도는 기다릴수록 올라간다」입니다. 판단을 미루더라도, 다음 장의 판단표로 「어디가 어려운가」만이라도 먼저 파악해 두어야 합니다.
5. 이전 대상 판단표
이전 대상은 「MSMQ의 후속 제품이 무엇인가」가 아니라, 실제로 쓰는 성질마다 고릅니다.
5.1. 먼저 요구를 네 질문으로 나눈다
- 큐의 상대(수신 측 처리)는 자사 데이터베이스를 갱신하는 처리인가?
- 분산 트랜잭션(DTC)을 쓰는가?(트랜잭션 큐+DB 갱신)
- 송신 측과 수신 측은 다른 머신·다른 거점인가? 오프라인 내성(store-and-forward)을 정말로 쓰는가?
- 메시지량은 실제로 어느 정도인가?(많은 업무 시스템에서는 하루 수천〜수만 건이며, 이는 어느 선택지에서도 여유가 있습니다)
5.2. 판단표
| 쓰는 성질 | 1순위 후보 | 이유와 주의 |
|---|---|---|
| 동일 시스템 안의 비동기 처리(수신 측은 DB 갱신) | DB의 테이블 큐 | 「메시지 꺼내기」와 「업무 데이터 갱신」을 같은 local transaction으로 확정할 수 있어 DTC가 필요 없어집니다. 기존 백업·감시·운영을 그대로 쓸 수 있습니다. 송신 측에서도 「업무 데이터 갱신과 메시지 행 추가」를 같은 트랜잭션에 둘 수 있으며, 이쪽은 이른바 Outbox 패턴입니다 |
| DTC로 큐와 DB 갱신을 atomic하게 하는 경우 | DB의 테이블 큐 | 분산 트랜잭션을 local transaction으로 바꾸는 것이 이전의 본질입니다. 클라우드 계열 큐는 DTC에 참여하지 못하므로, 이 요구가 있는 한 브로커 계열은 우회가 됩니다 |
| 여러 시스템·여러 언어 사이의 느슨한 결합 연동 | RabbitMQ(온프레미스) / Azure Service Bus(클라우드 가능) | 배포의 fan-out이나 라우팅은 브로커가 잘하는 분야입니다. RabbitMQ는 자체 운영 부하(이중화·업데이트)를 견적에 넣으십시오 |
| 거점 사이 오프라인 내성(store-and-forward) | Azure Service Bus 등+재전송 설계, 또는 거점 측 테이블 큐+동기화 | MSMQ의 「송신 측 로컬에 쌓아 나중에 도착」을 투명하게 대체하는 구조는 적습니다. 송신 측에서 쌓는 책임을 앱 쪽에 명시적으로 두는 설계로 바꿉니다 |
| 동일 프로세스 안의 생산자/소비자 | .NET의 Channels 등 in-memory 큐 | 애초에 프로세스 간 큐가 필요 없던 경우입니다. 프로세스 안에서 끝내고, 영속성이 필요하면 테이블 큐로 |
| Windows 서비스 사이의 프로세스 간 통신으로만 쓰는 경우 | named pipe 등 IPC | 다만 바꿀 수 있는 것은 양쪽이 항상 동시에 돌아가는 경우뿐입니다. 수신 측이 멈춰 있어도 MSMQ는(express 메시지라도) 송신을 받아 나중에 배달하지만, pipe는 즉시 실패합니다. 정지 중 축적에 기대고 있다면 재전송·버퍼를 앱 쪽에 구현하거나 큐를 남깁니다. 고르는 법은 「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);
꺼내기는 한 건만 「처리 중」으로 표시하면서 본문을 받습니다. READPAST를 붙이면 다른 worker가 잠근 행을 기다리지 않고 건너뛸 수 있습니다(여러 프로세스에서 동시에 돌릴 수 있습니다).
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이고 세션의 isolation level이 READ COMMITTED일 때(또는 READCOMMITTED 힌트를 함께 쓸 때) READPAST는 지정할 수 없다」고 명시하며, 그 경우의 대처로 READCOMMITTEDLOCK 힌트를 함께 적는 것을 듭니다.10 기본 isolation level은 READ COMMITTED이므로, READ_COMMITTED_SNAPSHOT만 켠 데이터베이스에서는 이 꺼내기가 「다른 worker의 행을 건너뛴다」는 수준이 아니라, 문 자체가 오류가 됩니다. 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이 같은 「단위(granularity) 힌트」 그룹이라 한 테이블에 둘 다 지정할 수 없기 때문입니다.10 READPAST가 건너뛸 수 있는 것은 행 잠금뿐이고 페이지 잠금은 건너뛰지 못하므로 ROWLOCK을 명시하고 싶지만, TOP (1)의 인덱스 seek로 잡는 1행에 대해 escalation이 일어나는 일은 사실상 없습니다. READ_COMMITTED_SNAPSHOT이 OFF임이 분명하고 ROWLOCK을 명시하고 싶다면 READCOMMITTEDLOCK 쪽을 빼십시오.
ORDER BY를 빼고 UPDATE TOP (1)로 쓰면 어느 행이 골라지는지는 정해지지 않습니다. UPDATE의 TOP은 대상 행을 정렬하지 않는다고 공식으로 명시되어 있으며, (Status, Id) 인덱스가 있어도 꺼내는 순서의 보장은 되지 않습니다.11 넣은 순서로 처리되는 것이 전제인 연동에서는, 오래된 job이 계속 뒤로 밀리는 형태의 장애가 됩니다.
다만 ORDER BY Id가 맞추는 것은 꺼내는 순서뿐입니다. 처리가 끝나는 순서는 맞춰지지 않습니다. READPAST는 「다른 worker가 잠근 행을 건너뛰기」 위한 지정이므로, worker A가 job 1을 잡고 오래 걸리는 동안 worker B는 job 2를 잡아 먼저 commit할 수 있습니다.
| 맞춰지는 것 | 맞춰지지 않는 것 |
|---|---|
어느 행을 먼저 꺼내는가(ORDER BY Id) |
어느 행이 먼저 업무 데이터에 반영되는가 |
「같은 수주 번호에 대한 갱신이 뒤바뀌면 곤란하다」처럼 반영 순서에 의미가 있는 연동에서는 이것으로는 부족합니다. 취할 수 있는 형태는 다음 둘 중 하나입니다.
- 소비자를 하나로 둔다. 병렬도를 버리고 순서를 취합니다. throughput이 충분하면 이것이 가장 단순하고 깨지기 어려운 형태입니다
- 키 단위로 나눈다. 수주 번호 등의 키로 worker를 고정하거나, 「같은 키의 행이 처리 중이면 꺼내지 않는다」는 조건을 꺼내기 쪽에 더해 키 안에서만 순서를 보장합니다. 키를 가로지르는 순서는 포기합니다
둘 다 하지 않는다면, 병렬로 돌린 시점에 전체 FIFO는 없다는 것을 이전 설계로 명시해 두십시오. MSMQ에서 옮길 때 여기는 특히 빠집니다. MSMQ 큐도 병렬로 수신하면 같은 일이 일어나지만, 「큐니까 순서대로」라는 이해 그대로 옮기면 테이블 큐로 바꾼 순간에 순서 문제가 생긴 것처럼 보입니다.
수신 측 worker는 이것을 업무 처리와 같은 local transaction 안에서 돌리기만 하면 됩니다(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); // 비었으면 polling 간격만 기다림
continue;
}
ApplyBusinessData(connection, tx, job); // ← 업무 데이터 갱신(본래 하려는 일)
MarkDone(connection, tx, job.Id); // Status = 2
tx.Commit(); // 「꺼내기」와 「업무 갱신」이 이 한 줄에서 동시에 확정된다
}
여기가 MSMQ+DTC의 대체가 되는 점입니다. MSMQ에서는 「큐에서 꺼내기」와 「DB 갱신」이 다른 리소스였기 때문에, 둘을 묶으려면 분산 트랜잭션(DTC)이 필요했습니다. 큐가 DB 테이블이면 둘 다 같은 데이터베이스에 대한 갱신이므로 보통의 local transaction으로 끝납니다. RabbitMQ나 Azure Service Bus를 넣으면 이 성질은 사라지고(큐와 DB는 다시 다른 리소스가 되고), 멱등성과 재전송을 앱 쪽에 만들어야 합니다.
실운영에서 더해야 하는 것은 다음 세 가지 정도입니다.
- 처리 중인 채 죽은 행의 회수.
Status = 1이면서StartedAt이 일정 시간보다 오래된 행을Status = 0으로 되돌리는 복구 처리를 정기 실행에 넣습니다. - 재시도 상한과 대피.
RetryCount가 상한을 넘은 행은 다른 테이블(또는Status = 9)로 모읍니다. MSMQ의 dead-letter queue에 해당하는 자리입니다. - 완료 행 청소.
Status = 2인 행을 정기적으로 삭제·아카이브합니다. 내버려 두면 테이블이 커지고 인덱스 효과가 떨어집니다.
송신 측에서는 업무 데이터 갱신과 메시지 행의 INSERT를 같은 트랜잭션에 넣습니다(Outbox 패턴). 이렇게 하면 「업무 데이터는 갱신됐는데 메시지가 나가지 않은」 불일치도 사라집니다.
강조하고 싶은 것은, 중소 규모 업무 시스템에서는 테이블 큐가 1순위 후보가 되는 경우가 많다는 점입니다. 메시지 큐 제품 비교 기사는 「RabbitMQ vs Kafka vs Service Bus」 형태가 되기 쉽지만, MSMQ 세대 시스템이 큐에 싣는 것은 많은 경우 「같은 DB를 갱신하는 비동기 job」이며, 그것은 DB 테이블과 polling(또는 알림)으로 충분하고, 게다가 더 단순하게 구현할 수 있습니다. 새 미들웨어를 하나 늘리는 운영 비용(감시·이중화·패치·담당자 교육)은 중소 체제에서 특히 무겁게 붙습니다.
6. 이전의 실무 절차
진행 방법은 레거시 마이그레이션의 정석대로 「관측한 뒤에 움직인다」입니다.
-
의존 현황 조사. 코드에서
System.Messaging참조와MessageQueue생성 지점을 가려냅니다. 더불어 WCF 구성 파일의netMsmqBinding/msmqIntegrationBinding, native API(MQSendMessage등의MQ*함수)나 COM을 통한 호출도 검색하십시오. System.Messaging을 참조하지 않아도 MSMQ에 의존하는 경로는 있을 수 있습니다. 확인할 관점은 (a) 큐 경로(로컬인지 원격인지, private인지 public인지), (b) 트랜잭션 큐인지, 메시지를 Recoverable로 두는지(어느 쪽도 아니면 현행 시스템은 재시작으로 메시지가 사라진다는 전제로 돌아가고 있음), (c) formatter(XmlMessageFormatter / BinaryMessageFormatter / ActiveXMessageFormatter), (d) journal·dead-letter queue 사용, (e) 누가 만들고 삭제하는지(설치 프로그램인지 앱인지)입니다.현황 조사는 확인만 해서는 이전 판단에 쓰이지 않습니다. 다음 형태로 큐 하나당 한 행을 채워, 그대로 이전 방침의 근거로 쓰십시오(그대로 품의 자료 별첨이 됩니다).
관점 어디를 보는가 기록할 내용 판정에 미치는 영향 (a) 큐 경로 MessageQueue에 넘기는 경로 문자열, 구성 파일의 접속 대상,FormatName:지정로컬/원격 구분, private/public 구분, 상대 머신 이름 원격·거점 사이면 store-and-forward 필요 여부를 판정(5.2의 「오프라인 내성」 행). 로컬에서 끝나면 5.2의 첫 행에 해당합니다 (b) 트랜잭션과 Recoverable 큐 생성 지점(트랜잭션 큐인지), 송신 때 Recoverable을 지정하는지, DTC를 쓰는지 트랜잭션 큐 여부, Recoverable 지정 유무, DTC 참여 유무 DTC가 있으면 이전 대상은 테이블 큐가 거의 확정입니다. 둘 다 없으면 현행이 「재시작으로 메시지가 사라지는 전제」로 운영되고 있음을 명시합니다 (c) formatter XmlMessageFormatter/BinaryMessageFormatter/ActiveXMessageFormatter지정 지점쓰는 formatter와 메시지 본문 형식 BinaryFormatter 계열이면 JSON으로의 대체가 필수입니다(절차 2). 이전 작업량의 주된 요인이 됩니다7 (d) journal·dead-letter queue 큐 속성(journal 유효 여부), 시스템 큐 내용, 운영 절차서 journal 이용 유무, dead-letter queue를 실제로 보는지 「실패 메시지를 어떻게 다루는지」가 이전 대상 설계 요구가 됩니다(테이블 큐라면 대피 테이블) (e) 누가 만들고 삭제하는지 설치 프로그램, 배포 스크립트, 앱 시작 시 Create호출생성 주체와 권한 설정을 누가 하는지 이전 때 「새 큐를 누가 준비하는가」가 그대로 바뀝니다. 현상 유지를 고를 때는 키팅 절차에 옮겨 적습니다(7장) - 메시지 형식을 정한다. 이전 후 형식은 JSON을 기본으로 합니다. 가져와서는 안 되는 것은 BinaryMessageFormatter 같은 BinaryFormatter 계열 serialization입니다. BinaryFormatter는 .NET 9에서 삭제되었고, 보안상으로도 권장되지 않습니다.7 한편 Protocol Buffers나 MessagePack처럼 사양이 독립적으로 유지되는 binary 형식을 쓰고 있다면, 그 형식을 새 시스템으로 그대로 가져가는 것 자체는 문제가 없습니다.
- 수신 측부터 옮긴다. 새 큐(예를 들어 테이블 큐)를 먼저 준비하고, 수신 측 처리를 새 큐에 맞춘 뒤 송신 측을 전환합니다. 이전 기간 중에는 옛 MSMQ에서 새 큐로 메시지를 옮기는 작은 브리지(이것은 .NET Framework 그대로여도 됩니다)를 끼우면, 송신 측을 일제히 바꾸지 않아도 됩니다. 다만 브리지는 「MSMQ에서 꺼내기」와 「새 큐에 쓰기」 사이에서 죽으면 메시지를 잃거나 이중으로 흘립니다. 옮기기 전 큐가 트랜잭션 큐라면, MSMQ 쪽을 트랜잭션 수신으로 두어 쓰기 실패 때 되돌릴 수 있게 하고, 새 큐 쪽은 메시지 ID로 중복을 걸러 내는(멱등한) 설계로 하는 것이 최소 조건입니다. 옮기기 전 큐가 비트랜잭션 큐인 경우에는 이 수가 통하지 않습니다. 큐의 트랜잭션 속성은 만들 때 정해지고 나중에 바꿀 수 없기 때문입니다. 그 경우에는 「Peek로 읽기 → 새 큐에 멱등하게 쓰기 → 써진 것을 확인한 뒤 제거」 순으로 짜입니다(제거 전에 죽어도 중복은 새 큐 쪽 멱등성이 흡수합니다). 어느 쪽 만들기도 규모에 안 맞으면, 브리지를 끼우지 않고 절차 5의 「비운 뒤 전환」으로 모으는 편이 안전합니다.
- 동작을 고정한 뒤 바꾼다. 큐 처리는 타이밍 의존 버그가 나오기 쉬운 영역입니다. 이전 전에 「이 입력 메시지면 이 결과」라는 특성 테스트를 마련해 두면, 교체 후 검증이 기계적이 됩니다(「특성 테스트로 동작을 고정한 뒤 리팩터링한다」).
- 큐 내용이 빈 상태에서 전환한다. 메시지가 남은 상태의 전환은 이중 처리·누락의 온상입니다. 계획 정지로 큐를 비운 뒤 전환하는 것이, 결국 가장 안전하고 빠른 방법입니다.
7. 현상 유지를 고를 때의 최소 조건
「앱을 .NET으로 올리는 계획이 없고, 비용 대비 효과에서도 당장은 옮기지 않는다」는 판단은 조건부로는 합리적입니다. 그 경우의 최소 조건을, 품의·리뷰 자료에 그대로 옮겨 적을 수 있는 체크리스트 형태로 듭니다. 네 가지 모두에 표시가 붙어 비로소 「현상 유지」가 선택지가 된다고 보십시오.
| 확인 | 최소 조건 | 구체적으로 할 일 | 충족하지 않으면 무엇이 일어나는가 |
|---|---|---|---|
| □ | 키팅·복원 절차에 MSMQ를 명시한다 | MSMQ는 Windows의 선택 기능입니다. 「Windows 기능 켜기」 또는 DISM/PowerShell로 활성화하는 절차를 환경 구축 절차서에 적어 둡니다 | PC·서버 교체 때 활성화를 잊어, 이전 당일에 원인 불명으로 동작하지 않습니다 |
| □ | 큐 길이를 감시한다 | 체류 건수의 임계값 감시와 알림을 넣습니다. dead-letter queue와 journal queue도 대상에 포함합니다 | 수신 측이 멈춰도 오류가 되지 않고 계속 쌓이므로, 아무도 모르는 채 업무가 멈춥니다 |
| □ | 구성을 문서화한다 | 큐 경로, 권한, 트랜잭션 유무, formatter, journal 설정을 기록합니다(6장의 현황 조사 표를 그대로 쓸 수 있습니다) | 장래의 이전 견적이 조사부터 다시가 되어, 정밀도와 작업량 모두 나빠집니다 |
| □ | 연 1회 판단을 다시 본다 | 「deprecated 목록에 올랐는지」「OS 업데이트로 동작이 바뀌지 않았는지」를 연차로 확인합니다 | deprecated 고지가 나온 뒤에야 허둥지둥 움직이게 됩니다 |
네 번째는 특히 가볍게 보기 쉽지만, 이 연차 확인이 있기 때문에 고지가 나온 뒤에도 허둥대지 않아도 됩니다. 반대로 이 네 가지가 채워지지 않는 환경에서의 현상 유지는 「보이지 않은 채 멈추기를 기다리는 것」과 같습니다.
8. 정리
- MSMQ는 2026년 7월 시점에서 공식으로 deprecated가 아니며, OS 기능으로는 존속합니다. 「이미 폐지」라는 인식은 정확하지 않습니다.12
- 한편 System.Messaging은 .NET Framework 전용으로 멈췄고, 호환 팩에도 들어 있지 않으며, .NET(Core 이후)으로의 공식 길은 없습니다. CoreWCF 자체에는 지원 정책이 있으나, 그 MSMQ transport는 System.Messaging의 커뮤니티 이식에 의존합니다.345
- 판단의 축은 「앱을 .NET으로 올릴지」입니다. 올린다면 큐도 함께 옮기는 것이 원칙(P/Invoke 등으로의 수명 연장은 유지보수 부담과의 교환)이고, 현상 유지라면 감시와 문서를 조건으로 성립합니다.6
- 이전 대상의 1순위 후보는 DB의 테이블 큐입니다. MSMQ+DTC의 정합성은 local transaction으로 바꾸는 것이 본질이며, 브로커 제품 도입은 그다음에 검토합니다.
- BinaryFormatter 계열 serialization은 .NET 9에서의 삭제로 새 시스템에 가져올 수 없습니다. 이전 때는 JSON을 기본으로 하되, protobuf 등 독립적으로 유지되는 형식이면 그대로여도 됩니다.7
- 수신 측→브리지→송신 측 순으로 전환하고, 특성 테스트로 동작을 고정한 뒤 바꾸는 것이 사고를 피하는 진행입니다.
관련 기사
- VB6 앱은 언제까지 돌아가는가 ── 런타임 지원 현황과 현실적인 .NET 마이그레이션 진행
- .NET Framework→.NET 마이그레이션 전 체크리스트
- 특성 테스트(Characterization Test)로 동작을 고정한 뒤 리팩터링한다
- Windows의 IPC(프로세스 간 통신) 판단표
- TCP에서 Send한 단위마다 Receive할 수 있다는 오해 ── 바이트 스트림으로 다루기 위한 수신 설계
- 소스도 자료도 없는 시스템의 보수를 어떻게 맡을 것인가
- 업무 앱의 DB 스키마 마이그레이션과 버전 관리
관련 상담 영역
합동회사 고무라소프트에서는 MSMQ를 포함한 레거시 구성의 현황 조사와 이전 계획, .NET Framework에서 .NET으로의 마이그레이션, 큐 처리를 포함한 업무 시스템 개선·유지보수를 다룹니다.
참고 링크
-
Microsoft Learn, Deprecated features in the Windows client. Windows 클라이언트에서 적극적 개발이 종료된(deprecated) 기능의 공식 목록. 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에서 제거된 기능과 개발 종료(deprecated) 기능의 공식 목록. Windows Server 2025 탭을 포함한 2026년 7월 시점의 목록에 MSMQ가 실려 있지 않다는 점, deprecated 구성 요소도 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으로 이식하는 커뮤니티 주도 프로젝트이며 Microsoft가 공식 지원 정책을 제공한다는 점, 큐 계열 transport의 일환으로 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) 등 안전한 serialization 형식이 안내된다는 점에 대해. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn(아카이브), Express and Recoverable Messaging, System-Generated Queues, Message Queuing (MSMQ). express 메시지가 배송 중에도 배송 후에도 RAM에 유지되며, 메시지가 있는 컴퓨터의 정지나 MSMQ 서비스 정지로 잃는다는 점, recoverable 메시지가 송신 측과 중계하는 각 컴퓨터에서 디스크에 쓰이고 목적지 큐에서도 디스크에 유지된다는 점, public queue가 디렉터리 서비스에 등록되는 한편 private queue는 로컬 컴퓨터에만 등록되어 디렉터리 서비스에는 공개되지 않는다는 점, 큐에서 꺼낸 메시지나 송신 완료 메시지의 사본을 유지하는 journal queue와 배달하지 못한 메시지를 유지하는 배달 불능(dead-letter) queue가 MSMQ가 생성하는 시스템 큐라는 점에 대해. ↩
-
Microsoft Learn(아카이브), Message Queuing Functions 및 MQSendMessage, MQReceiveMessage. MSMQ의 Win32 native API(MQCreateQueue, MQSendMessage, MQReceiveMessage 등)가 C/C++ 애플리케이션용으로 문서화되어 있으며, 큐 생성·송신·수신을 managed API를 거치지 않고 할 수 있다는 점에 대해. ↩
-
Microsoft Learn, 테이블 힌트 (Transact-SQL).
READPAST가 다른 트랜잭션이 잠근 행을 읽지 않고 건너뛰는 힌트이며, 건너뛸 수 있는 것은 행 수준 잠금뿐이고 페이지 수준 잠금은 건너뛰지 못한다는 점,READ COMMITTED또는REPEATABLE READisolation level에서만 지정할 수 있다는 점에 대해. 특히 「READ_COMMITTED_SNAPSHOT데이터베이스 옵션이ON으로 설정되어 있고, 또한 (a) 세션의 트랜잭션 isolation level이READ COMMITTED이거나, (b) 쿼리에서READCOMMITTED테이블 힌트도 지정되어 있는, 어느 쪽이든 참인 경우READPAST테이블 힌트는 지정할 수 없다. 이러한 경우에READPAST힌트를 지정하려면READCOMMITTED테이블 힌트가 있으면 삭제하고, 쿼리에READCOMMITTEDLOCK테이블 힌트를 포함한다」는 기술에 대해.READCOMMITTEDLOCK이READ_COMMITTED_SNAPSHOT설정과 관계없이 잠금에 의한READ COMMITTED를 하게 하는 힌트라는 점, 그리고 한 테이블에 대해 단위(granularity) 힌트(PAGLOCK/NOLOCK/READCOMMITTEDLOCK/ROWLOCK/TABLOCK/TABLOCKX)를 둘 이상 지정할 수 없다는 점도 같은 페이지를 참조합니다. 데이터베이스 측 설정은 sys.databases의is_read_committed_snapshot_on으로 확인할 수 있습니다. ↩ ↩2 -
Microsoft Learn, TOP (Transact-SQL). INSERT·UPDATE·MERGE·DELETE에서 TOP을 쓸 때 참조되는 행이 어떤 순서로 늘어놓아지는 것은 아니라는 점, 그리고 순서를 정하고 싶을 때는 TOP과 ORDER BY를 가진 하위 쿼리를 쓴다는 점에 대해. ↩
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
VB6 앱은 언제까지 동작하는가 ── 런타임 지원 현황과 현실적인 .NET 이전 진행 방법
VB6 앱은 언제까지 동작할까요. 런타임은 Windows 11에서도 동작 대상이고 IDE는 지원이 종료된 현황을 정리하고, 전면 재작성·자동 변환·단계 이전의 판단표, 이전 전 자산 파악, VB6와 .NET의 비호환까지 정리합니다.
인수는 왜 깨지는가 ── Windows 명령줄 인수의 규칙
Windows에는 인수의 배열이 존재하지 않으며, CreateProcess에 전달되는 것은 한 줄의 문자열이고 분할은 받는 쪽이 합니다. CommandLineToArgvW·CRT·.NET의 분할 규칙과 .NET의 ArgumentList, C++에...
멀티스레드 실무 베스트 프랙티스 .NET 편 ── 스레드를 늘리기 전에 정해 둘 것
「스레드를 만들었더니 가끔 죽거나 멈춘다」를 막는 설계의 정석을 .NET/C# 대상으로 정리합니다. 스레드를 직접 만들지 않고 Task에 맡기기, 공유 가변 상태 줄이기, 락의 규율, CancellationToken으로 정지 설계하기, UI 스레...
WMI/CIM을 C#·PowerShell에서 쓰기 ── 하드웨어 정보 가져오기·프로세스 모니터링·원격 조회의 실무 가이드
PC 시리얼 번호 조회, 디스크 여유 공간 모니터링, 프로세스 시작 감지의 흔한 답이 WMI/CIM입니다. Get-CimInstance 등 CIM cmdlet 사용법과 구 Get-WmiObject에서의 이전, C#의 System.Managemen...
Windows의 TPM이란 무엇인가 ── 그림으로 보는 「키를 밖으로 내보내지 않는 금고」와 측정 부팅
TPM을 그림으로 설명합니다. 키를 칩 밖으로 내보내지 않는 구조, PCR과 측정 부팅, BitLocker와 Windows Hello에서의 쓰임, dTPM·fTPM·Pluton의 차이, Get-Tpm으로 확인하는 방법, 복구 키를 요구받았을 때의...
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
ActiveX 이관
COM / ActiveX / OCX 자산을 유지할지, 감쌀지, 교체할지의 단계적 판단을 정리한 토픽 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
기존 자산 활용 & 이관 지원
COM / ActiveX / OCX 자산, 네이티브 코드, 32비트 의존성을 유지하면서 단계적인 이관 계획을 지원합니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- MSMQ는 폐지되었습니까?
- 아닙니다. 2026년 7월 시점에서 Windows 클라이언트의 deprecated 기능 목록에도, Windows Server의 「제거된 기능·개발 종료 기능」 목록에도 MSMQ는 없습니다. Windows의 선택 기능으로 현행 OS에도 포함되어 있습니다. 「폐지되었다」는 표현이 퍼진 이유는 .NET 쪽 상황과 섞여 있기 때문입니다. MSMQ를 다루는 표준 클래스 라이브러리 System.Messaging은 .NET Framework에만 있고, .NET(Core 이후)에는 이식되지 않았습니다. 즉 정확한 상태는 「OS 기능으로는 남아 있으나, 현대 .NET에서 쓰는 공식 수단은 없다」입니다.
- .NET 8이나 .NET 10에서 MSMQ를 쓰는 방법은 없습니까?
- 공식 managed API는 없습니다. System.Messaging은 .NET Framework 4.8.1까지의 API이며, Windows 호환 팩(Microsoft.Windows.Compatibility)에도 들어 있지 않습니다. MSMQ의 native Win32 API(MQSendMessage 등)를 P/Invoke로 호출하는 것은 기술적으로 가능하지만, formatter와 트랜잭션 연동을 포함한 wrapper를 직접 만들고 유지보수해야 합니다. 커뮤니티 쪽에서는 CoreWCF 프로젝트가 MSMQ transport용 패키지(CoreWCF.MSMQ)를 공개하고 있으나, 이는 큐를 통해 호출되는 WCF 서비스를 현대 .NET에서 호스트하기 위한 것(WCF 서버 측 이식)이지, System.Messaging을 대체하는 범용 큐 API도, 송신 측 클라이언트의 대체도 아닙니다. 구현 또한 .NET Framework판 System.Messaging의 커뮤니티 이식에 의존합니다. 검증이나 임시 수명 연장의 선택지는 됩니다. CoreWCF 자체에는 Microsoft의 공식 지원 정책이 있지만, 이 MSMQ 구현이 의존하는 System.Messaging의 커뮤니티 이식까지 같은 보장이 있는 것은 아니므로, 업무 시스템의 전제로 둘 때는 쓰는 버전이 지원 정책 대상인지와 의존 부분의 취급을 확인한 뒤 판단해야 합니다. 실무적으로는 앱을 .NET으로 올리는 시점에 큐도 옮기는 것이 본류입니다.
- 이전 대상은 RabbitMQ와 Azure Service Bus 중 어느 쪽이 낫습니까?
- 그 이분법 앞에, 데이터베이스 테이블을 큐로 쓰는 안을 검토하십시오. MSMQ를 쓰는 중소 규모 업무 시스템의 상당수는 큐의 상대가 자사 데이터베이스를 갱신하는 처리입니다. 그 경우 테이블 큐라면 업무 데이터와 같은 local transaction으로 「메시지 꺼내기」와 「업무 데이터 갱신」을 확정할 수 있어, MSMQ+분산 트랜잭션(DTC)으로 지키던 정합성을 더 단순한 구조로 바꿀 수 있습니다. 테이블 큐로 부족한 요구, 예를 들어 여러 시스템 사이의 느슨한 결합 배포나 높은 throughput이 필요할 때, 온프레미스 요구가 강하면 RabbitMQ, 클라우드에 둘 수 있으면 Azure Service Bus를 검토하는 순서가 실패하기 어렵습니다.
- 당분간 .NET Framework 그대로 현상 유지하는 판단은 있습니까?
- 조건부로는 있습니다. .NET Framework 4.8은 Windows 구성 요소로서 OS 수명 주기를 따라 지원되고, MSMQ 자체도 deprecated가 아니므로 「계속 돌아간다」는 전망은 세울 수 있습니다. 다만 현상 유지를 고를 때는 키팅 절차에 MSMQ 기능 활성화를 명시하고, 큐 길이와 journal 감시를 넣고, 메시지 형식과 연결 구성을 문서화하고, 담당자 이동에 대비해 복구 절차를 남기는 네 가지를 최소한 하십시오. 현상 유지의 진짜 위험은 기술이 아니라 「만질 사람이 없어지는 것」입니다.