본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 02. 19:34

Claude Opus를 사용하여 .NET Framework를 .NET 9로 마이그레이션하며 발견한 5가지 놀라운 점

요약

Claude Opus를 활용하여 레거시 .NET Framework 프로젝트를 .NET 9로 마이그레이션하는 실험적 워크플로우를 소개합니다. 수동 작업 대신 AI를 통해 csproj 파일 변환 및 초기 코드 변경 사항을 제안받음으로써 개발자의 역할을 설계자로 전환하는 과정을 다룹니다.

핵심 포인트

  • Claude Opus를 활용한 SDK 스타일 csproj 파일의 성공적인 자동 변환
  • 레거시 라이브러리 교체 및 마이그레이션 계획 수립 지원
  • 반복적인 컨텍스트 스위칭을 줄여주는 AI 기반 개발 워크플로우
  • AI 제안을 검토하고 수정하는 설계자로서의 개발자 역할 변화

Claude Opus를 사용하여 .NET Framework를 .NET 9로 마이그레이션하며 발견한 5가지 놀라운 점

이 작은 실험을 하기 전 저의 워크플로우는 .NET Framework에서 .NET 9로 마이그레이션할 때의 아주 전형적인 방식이었습니다. Visual Studio 2026을 열고, 새로운 SDK 스타일 프로젝트를 생성하고, 파일들을 복사한 다음, 빨간색 물결선(red squiggles)의 벽에 부딪히는 것이었죠. 저는 csproj 파일을 수동으로 업데이트하고, System.Web이나 ConfigurationManager에 대응하는 현대적인 라이브러리를 찾아다니며, 문서와 Stack Overflow, 그리고 IDE 사이를 끊임없이 오가며 몇 시간을 보냈습니다. 그것은 컨텍스트 스위칭(context switching)의 연속이었고, 종종 그냥 막연한 추측에 의존해야 하는 고된 작업이었습니다.

이제 저의 초기 작업 방식은 완전히 달라 보입니다. 여전히 Visual Studio 2026이나 Rider 2026을 사용하고 있지만, 첫 단계는 전체 레거시 프로젝트 구조와 관련 코드 파일들을 Claude Opus 4.7에 입력하는 것입니다. 저는 Claude Opus에게 새로운 csproj를 생성하고, 초기 코드 변경 사항을 제안하며, 심지어 마이그레이션 계획의 개요를 작성하도록 프롬프트(prompt)를 입력합니다. 빨간색 물결선은 여전히 나타나지만, 그것은 절망적인 빈 캔버스가 아니라 개선을 위한 _시작점_이 됩니다. 마치 매우 유능하고 약간은 자신감이 넘치는 주니어 개발자가 허드렛일을 대신 해주고, 저는 검토하고 수정하는 설계자(architect)가 된 것 같습니다.

솔직히 저는 회의적이었습니다. 저희 팀은 꽤 규모가 큰 .NET Framework 4.8 웹 서비스를 보유하고 있습니다. 아주 오래된 것은 아니지만, 커스텀 설정이 있고 다양한 유틸리티 함수를 위해 System.Web에 의존하고 있어 확실히 노후화된 상태였습니다. 목표는 현대적인 성능과 기능을 활용하기 위해 이를 .NET 9(당연히 C# 13 포함)로 전환하는 것이었습니다. 저는 이것이 ai migration dotnet이 실제로 성과를 낼 수 있는지, 아니면 그저 과장된 광고(hype)에 불과한지를 확인하기 위한 최적의 대상이라고 생각했습니다.

Claude Opus 4.7이 초기 작업에서 맞춘 부분 (그리고 틀린 부분)

나의 첫 번째 실제 테스트는 csproj 파일과 몇 개의 핵심 C# 파일을 포함한 전체 솔루션 구조를 Claude Opus 4.7에 입력하고, net framework to net 9 변환을 요청하는 것이었습니다. 나를 놀라게 했던 점은 기본적인 csproj 업그레이드를 얼마나 잘 처리했는가 하는 점이었습니다. 모델은 대상 프레임워크 (target frameworks), 패키지 참조 (package references), 그리고 대부분의 ItemGroup 변경 사항까지 정확하게 식별했습니다. 솔직히 말해서 첫 번째 시도에서 완전히 실패할 것이라고 예상했지만, 생성된 SDK 스타일 프로젝트 파일은 견고한 토대가 되었습니다.

<!-- Claude Opus 4.7에 대한 초기 프롬프트 -->
<prompt>
.NET Framework 4.8 C# 프로젝트가 있습니다. 이 csproj를 SDK 스타일의 .NET 9 csproj로 변환해 주세요.
...
// Claude가 제안한 MyService.cs 변경 사항
// 참고: 이것은 시작점이며, IConfiguration 및 DI(의존성 주입)와의 수동 통합이 필요합니다.
using Microsoft.Extensions.Configuration; // 추가됨
using Microsoft.AspNetCore.Http; // HttpContext 교체를 위해 (ASP.NET Core로 가정)
using System.Collections.Generic;

namespace MyLegacyService // 네임스페이스 유지, 프로젝트 구조 가정
{
public class MyService
{
// IConfiguration 주입을 위한 플레이스홀더
private readonly IConfiguration _configuration;
private readonly IHttpContextAccessor _httpContextAccessor; // HttpContext 접근을 위해

public MyService(IConfiguration configuration, IHttpContextAccessor httpContextAccessor) // 생성자 추가
{
    _configuration = configuration;

...


}

예상했던 대로, Claude가 실수를 범한 부분은 더 깊은 통합 패턴 (integration patterns)이었습니다. Claude는 ConfigurationManager.AppSettings를 제거해야 한다는 점은 정확히 식별했지만, 의존성 주입 (Dependency Injection, DI)이나 해당 IConfiguration 인스턴스가 새로운 ASP.NET Core 프로젝트에서 실제로 어떻게 전달될지에 대한 고려 없이 이를 직접적인 IConfiguration 접근 방식으로 대체해 버렸습니다. 마찬가지로 HttpContext.Current에 대해서는 IHttpContextAccessor를 제시했는데, 이는 올바른 방향이지만 다시 한번 완전한 DI 설정은 제공하지 못했습니다. 강력한 힌트는 되었지만, 즉시 적용 가능한 (drop-in) 해결책은 아니었습니다. 제가 아키텍처가 아닌 코드만 제공했다는 점을 감안하면 타당한 결과였지만, **ASP.NET Core 앱이 구성되는 방식에 대한 맥락 (context)**이 초기 이해 과정에서 누락되어 있었습니다.

반복적인 댄스: 미묘한 차이와 의존성을 위한 프롬프팅 (Prompting)

...

프롬프트 (prompt)
내 .NET 9 프로젝트는 의존성 주입 (Dependency Injection)을 사용합니다. 현재 ConfigurationManager.AppSettings에 접근하는 정적 유틸리티 클래스 'LegacyConfigHelper'가 있습니다. 현대적인 ASP.NET Core 9 애플리케이션에서 DI 원칙을 위반하지 않으면서, 이 정적 클래스가 DI로부터 IConfiguration을 사용하도록 리팩터링하거나 정적 컨텍스트에서 구성을 사용하는 적절한 패턴을 제공하려면 어떻게 해야 하나요? 코드 예시를 제공해 주세요.
// 정적 구성 접근을 위해 Claude가 제안한 패턴 (단순화됨)
using Microsoft.Extensions.Configuration;
using System;
...

이 패턴은 장기적으로 이상적이지는 않지만, 저에게 가교 역할을 해주었습니다. 덕분에 정적 헬퍼 (static helpers)들의 완전한 리팩터링은 뒤로 미루고, dotnet build를 통해 애플리케이션을 빠르게 컴파일하고 실행할 수 있었습니다. 저는 크고 방대한 파일을 한꺼번에 쏟아붓는 것보다, 작고 집중된 코드 블록과 매우 구체적인 문제 설명을 제공하는 것이 훨씬 더 나은 결과를 낳는다는 것을 발견했습니다. 개인마다 차이가 있을 수 있겠지만, 저에게 있어 ai migration dotnet에 대한 이러한 세밀한 접근 방식이 핵심이었습니다. 또한 Claude가 힘든 작업을 마친 후에는, 파일 내의 작은 리팩터링을 위해 Visual Studio 2026 내에서 Copilot Edits를 사용하기 시작했습니다.

디버깅 및 개선을 위해 내가 결정한 설정

최종적인 설정은 디버깅 및 테스트 실행, 특히 새로운 C# 13 기능들을 활용하기 위한 주요 IDE로 Visual Studio 2026을 사용하는 것이었습니다. 더 큰 규모의 리팩터링(Refactor)을 수행하거나 단일 파일 이상의 컨텍스트(Context)를 Claude에게 제공해야 할 때는 Cursor 0.42+를 사용했습니다. 여러 파일을 인덱싱하고 더 큰 컨텍스트 윈도우(Context Window)를 제공하여 "코드베이스와 채팅(chat with your codebase)"할 수 있는 Cursor의 능력(Claude Opus 4.7을 위해 MCP, Model Context Protocol 활용)은 매우 귀중했습니다. 특정 System.Web 메서드의 모든 사용 사례를 찾아 프로젝트 전체에 걸쳐 교체 사항을 제안하도록 요청할 수 있었는데, 이는 수동 검색보다 훨씬 효율적이었습니다.

Claude가 변환한 코드 덩어리가 생기면, 저는 그것을 Visual Studio에 붙여넣고, Copilot Edits가 사소한 구문 수정(Syntax fixes)을 제안하도록 한 뒤, 즉시 기존 유닛 테스트(Unit tests, 이 역시 Claude의 도움을 받아 마이그레이션 및 업데이트 중이었습니다)를 실행하는 프로세스를 거쳤습니다.

# 마이그레이션 단계를 검증하기 위한 테스트 실행 예시
# 마이그레이션된 서비스에 대한 테스트 프로젝트를 생성했다고 가정함
cd MyMigratedService.Tests
...

이러한 빠른 피드백 루프(Feedback loop)는 매우 중요했습니다. 저는 처음에 즉각적인 검증 없이 Claude의 출력물을 너무 많이 신뢰함으로써 실수를 저질렀습니다. 지난 화요일, 겉보기에는 올바른 IConfiguration 접근 방식이 계속 null을 반환하여 디버깅을 하고 있었는데, 알고 보니 Claude가 제 appsettings 구조와 일치하지 않는 GetSection 호출을 제안했다는 것을 깨달았습니다. 만약 제가 테스트를 작성해 두었다면(그 후에 작성했습니다), 빠른 dotnet test 한 번으로 즉시 잡아낼 수 있었을 것입니다. 저는 아직 Claude의 대규모 리팩터링과 함께 Copilot for Workspaces를 활용하는 최선의 방법을 찾아가는 중이지만, Claude의 거시적인 작업과 Copilot의 미세한 지원의 조합은 강력하다는 것이 증명되고 있습니다.

해당 서비스는 이제 대부분 .NET 9에서 실행되고 있지만, 아직 제거할 계획인 몇몇 LegacyConfigHelper 인스턴스들이 남아 있습니다. 남은 가장 큰 과제는 .NET Framework와 .NET 9 사이에서 변경된 리플렉션(Reflection) 패턴에 크게 의존하는 복잡한 커스텀 직렬화(Serialization) 라이브러리입니다.

만약 여러분이 상당한 규모의 .NET Framework 프로젝트를 .NET 9로 마이그레이션하기 위해 Claude나 Copilot for Workspaces와 같은 AI 모델을 사용해 보셨다면, 여러분이 겪었던 가장 큰 문제점(gotchas)이나 예상치 못한 성과(unexpected wins)에 대해 듣고 싶습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0