Unity 게임을 위한 .NET Core 서버 구축 (Part 2)
요약
본 영상은 Unity 게임 개발 환경에 연동되는 .NET Core 서버 구축의 후속 과정입니다. JSON 직렬화 라이브러리를 기본 유틸리티 대신 Newtonsoft로 변경하여 자유도를 높이고, 데이터베이스 처리를 위해 ORM(Object Relational Mapper)을 도입하는 방법을 다룹니다. 특히 Microsoft의 Entity Framework를 사용하여 SQL Server와 연동하고 사용자 정보를 저장할 준비를 합니다.
핵심 포인트
- JSON 직렬화에 Newtonsoft 라이브러리 사용으로 유연성 확보
- ORM (Object Relational Mapper)을 통해 DB 테이블을 객체처럼 처리 가능
- Microsoft Entity Framework를 활용하여 데이터베이스 접근 용이
- 사용자 인증/권한 부여를 위해 사용자 정보 저장 준비
안녕하세요, 돌아오신 것을 환영합니다. 이것은 커스텀 .NET Core 서버 구현의 파트 2입니다. 파트 1에서 제가 광범위한 주제들을 다루었다는 것을 눈치채셨을 겁니다. 파트 2에서는 실제로는 더 많은 주제들이 있습니다. 여러분이 흥미를 느낄 만큼만 보여드릴 것이니, 더 알고 싶다면 구글링 하셔야 할 겁니다. 저는 그것을 추천합니다. 자, 그럼 바로 시작하겠습니다. 첫 번째로 하고 싶은 것은 이전 영상에서 제가 했던 것을 수정하는 것입니다. 이전 비디오의 HTTP 클라이언트에서는 여기 유니티에 내장된 JSON 유틸리티를 사용하고 있습니다. 첫 번째 버전을 출시한 후 디스코드에서 누군가가 저에게 게임과 서버 모두에서 Newtonsoft를 사용하는 것이 어떨지 언급했는데, 매우 타당한 지적입니다. 제가 왜 그것을 생각하지 못했는지 모르겠습니다. 여러분의 Unity 게임으로 가서 패키지 관리자로 이동하고 Git에서 패키지를 추가하세요. 유니티에는 Newtonsoft의 공식 버전이 있으므로 직접 추가할 수 있습니다. 필요하면 설명란에 넣을게요. 그리고 이것이 들어가면 이 줄만 변경할 수 있습니다. JsonConvert [음악] 그리고 이것은 직렬화되고 타입 T가 될 것입니다. 여기 같은 매개변수를 넣을 수 있습니다. 이것도 복사합시다. 자, 이제 Newtonsoft로 바꿨습니다. 기본 JSON 유틸리티 대신에, 훨씬 더 많은 자유도를 얻게 됩니다. 이는 서버에서도 실제로 이러한 필드를 만들 필요가 없다는 것을 의미합니다. 실제로는 속성(properties)으로 가질 수 있습니다. 그러니 속성으로 만듭시다. 특히 나중 단계에서 필요한 것은 속성이므로 매우 유익할 것입니다. 그리고 마지막으로 정리해야 할 것이 하나 더 있습니다. 기억나실지 모르겠지만, 이 줄은 모델을 PascalCase로 반환하게 했으므로 제거합시다. 이게 다입니다. 오늘 우리가 다룰 첫 번째 주제는 데이터베이스이고,
우리가 그렇게 할 방법은 ORM(Object Relational Mapper), 즉 객체 관계 매퍼를 사용하는 것입니다. 실제로 이것이 의미하는 바는 데이터베이스의 테이블과 행을 마치 프로젝트 내의 객체처럼 다룰 수 있다는 것입니다. 예를 들어, 데이터베이스에 플레이어 테이블을 만들고, 쿼리하여 사용하면서, 마치 여기에서 플레이된 것처럼 사용할 수 있습니다. 그리고 실제로는 직접적인 데이터베이스 쿼리를 할 필요가 없습니다. 이 점이 훨씬 쉽게 만듭니다. 그래서 가장 인기 있는 ORM 중 하나는 Microsoft에서 만든 Entity Framework라는 것입니다. Entity Framework의 장점은 다양한 데이터베이스와 작동한다는 것입니다. 사람들이 이 단어를 어떻게 말하는지에 대해서는 의견이 분분하지만, 저는 SQL이라고 부릅니다. MySQL을 사용할 수도 있고, SQLite를 사용할 수도 있고, PostgreSQL을 사용할 수도 있습니다. 아주 많은 종류가 있습니다. 좋아요. 하지만 이번에는 MS SQL만 사용할 것이고, Azure에 호스팅된 데이터베이스가 있습니다. 그걸 설치해 봅시다. 마우스 오른쪽 버튼을 클릭하고 서버 관리 > NuGet 패키지에서 검색할 것입니다. Entity Framework를 검색하겠습니다. 보시다시피 여기 PostgreSQL도 있고, SQLite도 있습니다. 저는 SQL Server용 것을 사용합니다. 프레임워크에 맞는 것을 선택했는지 확인하세요. 저는 6을 사용합니다. 그걸 설치하고, 우리가 설치해야 할 또 다른 것도 있습니다. 바로 Design입니다. 잠시 후에 이것에 대해 말씀드리겠습니다. 그러니 'Any Framework Core Design'을 설치하는 것을 확실히 하세요. 데이터베이스에 무엇을 저장하고 싶나요? 제 생각에는 가장 먼저 사용자(user)를 저장해야 합니다. 왜냐하면 이 튜토리얼의 다음 부분이 실제로 인증(authentication)과 권한 부여(authorization)이기 때문에 사용자 정보가 필요합니다. 그래서 공유 라이브러리(shared library)에서 새 클래스를 만들고, 이것을 'User'라고 부르겠습니다. 여기에 몇 가지 속성(prop)을 만들 것입니다. 하나는 사용자 이름(username)이 될 것이고, 다음 것은 비밀번호 해시(password hash), 그리고 다음 것은 솔트(salt)가 될 것입니다. 아차, 솔트요. 사실 ID도 필요합니다. 그래서 ID에는 정수형(integer)을 사용할 것입니다. 이것들이 무엇인지는 잠시 후에 말씀드리겠습니다. 모르셔도 괜찮으니 계속 설정해 봅시다. 서버에서
새 파일을 만들고, 클래스를 만들 건데 이걸 GameDbContext라고 부를 거예요. 그리고 여기서 DbContext를 상속받을 겁니다. 지금은 좀 복잡하게 느껴질 수 있지만 참아주세요. 여기에 생성자를 만들 건데, 이 생성자는 DbContextOptions를 받습니다. 이건 제네릭 타입일 거고, 실제 DbContextOptions를 받아들입니다. 그리고 여기에서 기본(base) 생성자도 호출해야 합니다. 그래서 이 클래스가 무엇이냐면, 데이터베이스로 가는 게이트웨이라고 생각할 수 있어요. 우리가 ORM을 사용해서 데이터베이스를 다루는 방식이죠. 그리고 여기서는 데이터베이스에 어떤 것들이 들어갈지 선언하는 곳이기도 해요. 여기에 속성(prop)을 만들 건데, 이건 DbSet 타입일 거예요. DbSet은 데이터베이스의 테이블 중 하나를 호출할 때 사용하는 것이죠. 우리는 User를 사용할 거고, 이건 Users 타입일 겁니다. 이제 우리의 데이터베이스 게이트웨이가 생겼으니, 실제로 애플리케이션에게 우리가 이걸 사용하고 싶다고 알려줘야 합니다. 그래서 프로그램(Program)에서 다른 서비스를 주입(inject)하고 싶어요. 여기 위쪽에서 해봅시다. 빌더 서비스(builder.Services)를 사용합시다. 이 좋은 점은, 직접 서비스를 만들 때 AddScoped 같은 걸 사용할 수 있지만, 우리가 설정할 필요가 없는 내장 타입들이 많다는 거예요. 그중 하나가 Entity Framework 전용입니다. 그래서 AddDbContext를 할 건데, 이게 우리의 GameDbContext가 될 겁니다. 지금 단계에서는 매우 일반적이에요. 그냥 표준 Entity Framework 클래스일 뿐이죠. 여기 옵션(options)에서 하는 것은 '야, 우리 SQL Server를 사용하고 싶어'라고 말하는 거예요. 그리고 이건 우리 SQL Server에 연결할 연결 문자열입니다. 그래서 여기에 옵션을 할 건데, 이게 UseSqlServer입니다. 아까 설치했던 SQL Server 패키지죠. 이제 실제로 연결 문자열을 가져와야 합니다. 애플리케이션에 연결 문자열이나 어떤 종류의 비밀(secret)을 하드 코딩하는 것은 절대 안 됩니다. 이유가 많지만, 프로젝트에서 여러 사람과 함께 일한다고 상상해 보세요. 그들 모두가 이걸 다룰 수 있도록 설계된 건 아니거든요.
비밀을 알면, 방금 그 비밀을 하드 코딩하여 GitHub에 커밋한 것이 됩니다. 음, 이유가 백만 가지 있지만, 이것은 그중 하나일 뿐입니다. 그래서 우리가 할 수 있는 것은 빌더(builder)가 여기에서 설정 파일에 접근할 수 있게 해준다는 것입니다. 제가 곧 보여드릴 건데, 일단 이 빌더 구성(builder configuration)을 사용해서 연결 문자열(connection string)을 가져올 겁니다. 이것은 이미 내장되어 있습니다. 그리고 우리는 이것을 임시로 'db'라고 부를 겁니다. 이제 db 연결 문자열을 가져오는 거죠. 그런데 이걸 어디서 가져오냐고요? 바로 여기 appsettings라는 파일에서 가져옵니다. 저희에게는 appsettings와 appsettings.Development가 있습니다. 자, appsettings에 'connectionStrings'라는 작은 섹션을 만들고, 여기에 'db'라는 속성(property)을 만듭니다. 그리고 이것을 빈 문자열(blank string)로 설정합시다. 제가 이걸 빈 문자열로 만드는 이유는 실제로 배포할 때 환경 변수(environmental variables)를 배포 환경에 넣을 수 있기 때문입니다. 예를 들어 가상 머신(virtual machine)이나 Azure 같은 전용 웹 앱 서비스(dedicated web app service) 같은 곳에요. 그러면 그 정보가 여기에 주입(inject)될 겁니다. 그래서 실제로 여기에 넣을 필요는 없지만, 개발하는 동안에는 당연히 환경 변수를 여기저기 두고 싶지는 않잖아요. 저희에게는 appsettings.Development가 있습니다. 이 파일은 프로덕션(production)에서는 완전히 무시되지만, 개발할 때 임시 변수를 넣을 수 있게 해줍니다. 그래서 데이터베이스를 설정하는 방법은 보여드리지 않을 건데, 심지어 개발용으로도 저는 클라우드 기반 데이터베이스를 사용합니다. 왜냐하면 컴퓨터마다 바뀌기 때문에 어디로 가든 항상 같은 테스트 데이터를 원하기 때문입니다. 그냥 더 쉽습니다. 그래서 Azure SQL Server용의 긴 연결 문자열을 사용할 겁니다. 네, 서버를 설정하고 여기에 연결 문자열을 넣은 다음 이 튜토리얼을 계속 진행하세요. 그리고 참고로 말씀드리자면, 지금 배우고 계신 모든 플랫폼(for .NET Core)에서 이 개념들은 동일합니다. 하지만 appsettings라는 이 개념은 Node.js 서버에서도 있을 것이고, Rust 서버에서도 있을 것이고, C++ 서버에서도 있을 것입니다.
모든 서버에 있을 것이고, 이름만 다를 뿐입니다. 하지만 이 개념을 배우는 것이 중요하기 때문에 어떤 프레임워크를 사용하든지 관계없이 가치가 있습니다. 그리고 이것이 우리가 서버를 설정하는 데 필요한 모든 것입니다. 이제 '어떻게 실제로 테이블들을 데이터베이스에 넣나요?'라고 궁금해하실 수 있습니다. ORM의 가장 멋진 점은 코드 퍼스트 마이그레이션(code first migrations)을 할 수 있다는 것입니다. 즉, 여기서 코드를 작성하고 실제로 데이터베이스로 푸시하는 것입니다. 그리고 우리는 결코 데이터베이스를 수동으로 조작할 필요가 없습니다. 만약 여러분이 ORM을 사용하면서 테이블들을 수동으로 조작하고 있다면, 뭔가 잘못한 것입니다. 몇 단계 뒤로 돌아가서 올바른 방식으로 하세요. 그렇지 않으면 동기화 문제(syncing issues)가 발생하여 정말 악몽이 될 겁니다. 따라서 어떤 데이터베이스를 사용하든 다른 데이터베이스 탐색기(database explorer)를 갖게 되지만, 저는 Navicat을 사용하고 있고 이미 서버를 추가했습니다. 그래서 여기에 들어가서 실제 데이터베이스로 이동하겠습니다. Entity Framework를 사용하면 모델을 데이터베이스에 푸시하는 것이 매우 쉽습니다. 서버의 루트 폴더로 가서 명령 프롬프트 창을 엽니다. 저는 PowerShell을 사용하고 여기에서 net ef라고 입력합니다. 'ef'는 Entity Framework입니다. 그리고 migrations add를 합니다. 그리고 이것을 initial commit 또는 initial migration이라고 부르겠습니다. 옵션을 전달하는 것 같지 않은데, 여기서 base를 호출하고 있습니다만 사실은 with options와 함께 보내져야 합니다. 자, 이제 이게 하는 일은 이 구성을 여기에 보내는 것입니다. 여기로 보내고 그 다음 베이스 클래스에 옵션과 함께 보내는 것입니다. 그러면 Entity Framework가 스스로 설정할 수 있습니다. 죄송합니다, 헷갈렸다면 그냥 지우고 initial commit을 다시 하겠습니다. 네, 완료되었습니다. 이제 여기에 migrations 폴더와 이 initial migration이 있을 겁니다. 그리고 'up'에서 보시다시피 우리가 푸시하는 것은 debit context에 있는 user 객체를 찾았습니다. 그래서 이제 모든 것을 데이터베이스 스캐폴딩(scaffolding)하고 있습니다. 따라서
우리는 마이그레이션을 수행했습니다. net ef update를 사용해야 하는데, 이게 하는 일은 모든 마이그레이션 파일들을 살펴보고 데이터베이스의 모든 마이그레이션 상태를 확인해서, 아직 동기화하지 않은 나머지 부분들만 동기화해 주는 것입니다. 이렇게 하면 모든 테이블들이 밀려 들어옵니다. 이제 Navicat으로 가보면, 우리의 모든 것이 담긴 users 테이블이 생겼다는 것을 알 수 있습니다. 만약 이 테이블을 변경하고 싶다면, 여기 저희의 'thing'에서 단순히 수정하면 됩니다. 새로운 마이그레이션을 만들고 업데이트만 하면 되고, 그러면 자동으로 밀려 들어옵니다. 우리가 데이터베이스 테이블을 수동으로 바꿀 필요가 전혀 없습니다. 멋지죠? 그리고 이제 실제로 사용하려면, 데이터베이스 컨텍스트(database context)를 사용해야 합니다. 여기 플레이어 컨트롤러로 가보겠습니다. 기억하시겠지만, 우리는 의존성 주입(dependency injection)에 이걸 추가했습니다. 즉, 여기에 그냥 주입할 수 있습니다. 자, GameDbContext를 추가해 보겠습니다. 이 컨텍스트라고 부르죠. 여기서는 전역 변수처럼 만들 것입니다. 멋지네요. 이제 컨텍스트가 생겼습니다. 그리고 우리의 get 함수에서 테스트하는 것보다, 바로 생성자(constructor)에서 처리하겠습니다. 데이터베이스에 데이터를 추가하려면, 예를 들어 user = new user라고 합시다. 지금은 이 테이블밖에 없으니까요. 사용자 이름(username)을 'tarot'으로, 비밀번호(password)를 'password 69 baby'로 하겠습니다. 그리고 여기서는 context.Add()를 하고 우리의 사용자를 보냅니다. 우리는 프레임워크에게 무언가를 추가하고 싶다고 말했지만, 아직 커밋(commit)하지 않았습니다. 그래서 context.SaveChanges()라고 합니다. 사실 실행하기 전에 제가 알기로는 'salt'가 null이 아닌 값이고, 그리고 기억하시겠지만 여기 ID가 있습니다. 만약 ID를 특정하게 정수형(integer type)으로 이름을 붙이면, 엔티티 프레임워크(entity framework)가 그것을 키워드(keyword)로 사용하고 자동으로 ID를 증가시킵니다. 그래서 이것이 데이터베이스의 첫 번째 항목이 될 것이고, 아마도 ID는 1일 것입니다. 다음 것은 ID가 2일 것이고, 절대 충돌하지 않을 것이며 계속해서 늘어날 것입니다. 그리고 이것은 인덱싱(index)하는 매우 빠른 방법입니다.
모델은 있지만 몇 가지 보안 문제가 있습니다. 하지만 나중에 조금 다룰 수도 있으니, 어쨌든 디버그를 누르고 우리가 엔드포인트로 가보겠습니다. 이게 높은 수준의 내용입니다. 여기서 우리는 사용자를 만들었고, 사용자(user)를 컨텍스트에 추가하고 변경 사항을 저장했습니다. 좋습니다. 완료되었습니다. 이제 여기로 가서 모델을 새로고침하면 실제로 그 객체가 삽입된 것을 볼 수 있습니다. 이것이 바로 dvd 컨텍스트를 사용하는 방법입니다. 어느 파일에든 주입(inject)하고, 그냥 컨텍스트에 추가하기만 하면 됩니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 YouTube Tarodev (Unity 팁)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기