코젤브

[API Server] MVC 패턴과 Service, Repository 본문

컴공의 일상/C#

[API Server] MVC 패턴과 Service, Repository

코딩하는 젤리 2024. 7. 9. 18:44

웹서버.. 즉 API Server를 공부하다보면 늘 항상 나오는 MVC 패턴!

그리고 디렉토리 구조를 보면 추가적으로 나오는 Service, Repository!

spring, node.js, ASP.NET core 어떤 프레임워크를 사용해도 결국 패턴은 같으니까요 :)

 

매번 헷갈려서 제가 보려고 이 참에 간단하게 정리합니다.

MVC는 이제 다들 잘 알고 계시죠?

Model, View, Controller ...

 

그럼 이제 Service와 Repository는 정확하게 무슨 역할을 수행하느냐?!

제가 알고있는 내용을 줄글로 적어봤습니다.

 

보통 API Server에서 MVC 패턴을 따르는데, 이때 추가적으로 Model, View, Controller 외에도 Service와 Repository도 사용하곤 하잖아.
내가 이해하기에는 Controller가 요청을 받아 처리하는 역할 (구체적으로 http 메소드에 따라 적절한 작업을 실행하도록)을 담당, 이때 Repository에 구현한 로직을 Controller에서 호출하고 예외 처리 같은 부분만 담당하는 게 가장 깔끔하다고 알고있어.
Repository는 DB 접근 관련해서 CRUD를 처리하는 역할 (이때 클래스와 인터페이스로 구성되어있음)을 담당하고,
Service는 Repository가 제공하는 메서드를 활용해 로직을 구성하는 것(트랜젝션 관리 등)이라고 생각했어.

 

대충 맞나요..?

GPT는 맞다고 합니다. 아래에 내용을 보기 좋게 정리해보겠습니다!

 

1. Model

  • 역할: 애플리케이션의 데이터 구조를 정의합니다. 데이터베이스 테이블에 매핑되는 클래스들을 포함합니다.

 

2. View

  • 역할: 사용자 인터페이스를 담당하며, 데이터의 시각적 표현을 정의합니다. API 서버에서는 보통 JSON, XML 등의 데이터 포맷으로 응답을 반환합니다.
  • 예: ASP.NET Core에서는 View를 Razor 페이지나 JSON 결과로 표현할 수 있지만, API 서버에서는 주로 JSON 응답을 반환합니다.

 

3. Controller

  • 역할: 클라이언트로부터 요청을 받아 적절한 Service를 호출하여 응답을 반환합니다. HTTP 메서드(GET, POST, PUT, DELETE)에 따라 작업을 수행합니다.
using Microsoft.AspNetCore.Mvc;
using Server.Models;
using Server.Services;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Server.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UsersController : ControllerBase
    {
        private readonly IUserService _userService;

        public UsersController(IUserService userService)
        {
            _userService = userService;
        }

        // GET: api/users
        [HttpGet]
        public async Task<ActionResult<IEnumerable<User>>> GetUsers()
        {
            var users = await _userService.GetAllUsersAsync();
            return Ok(users);
        }

        // GET: api/users/5
        [HttpGet("{id}")]
        public async Task<ActionResult<User>> GetUser(int id)
        {
            var user = await _userService.GetUserByIdAsync(id);

            if (user == null)
            {
                return NotFound();
            }

            return Ok(user);
        }

        // POST: api/users
        [HttpPost]
        public async Task<ActionResult<User>> CreateUser(User user)
        {
            var insertedId = await _userService.CreateUserAsync(user);
            user.Uid = insertedId;
            return CreatedAtAction(nameof(GetUser), new { id = user.Uid }, user);
        }

        // PUT: api/users/5
        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateUser(int id, User user)
        {
            if (id != user.Uid)
            {
                return BadRequest();
            }

            var affectedRows = await _userService.UpdateUserAsync(user);

            if (affectedRows == 0)
            {
                return NotFound();
            }

            return NoContent();
        }

        // DELETE: api/users/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteUser(int id)
        {
            var affectedRows = await _userService.DeleteUserAsync(id);

            if (affectedRows == 0)
            {
                return NotFound();
            }

            return NoContent();
        }
    }
}

 

4. Repository

  • 역할: 데이터베이스와의 상호작용을 담당하며, CRUD 연산을 처리합니다. 인터페이스와 클래스로 구성됩니다.
  • 예: IUserRepository, UserRepository
// Interfaces/IUserRepository.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Server.Models;

namespace Server.Interfaces
{
    public interface IUserRepository
    {
        Task<IEnumerable<User>> GetAllUsersAsync();
        Task<User> GetUserByIdAsync(int uid);
        Task<int> AddUserAsync(User user);
        Task<int> UpdateUserAsync(User user);
        Task<int> DeleteUserAsync(int uid);
    }
}

// Repositories/UserRepository.cs
using SqlKata.Execution;
using System.Collections.Generic;
using System.Threading.Tasks;
using Server.Interfaces;
using Server.Models;

namespace Server.Repositories
{
    public class UserRepository : IUserRepository
    {
        private readonly QueryFactory _db;

        public UserRepository(QueryFactory db)
        {
            _db = db;
        }

        public async Task<IEnumerable<User>> GetAllUsersAsync()
        {
            return await _db.Query("Users").GetAsync<User>();
        }

        public async Task<User> GetUserByIdAsync(int uid)
        {
            return await _db.Query("Users").Where("Uid", uid).FirstOrDefaultAsync<User>();
        }

        public async Task<int> AddUserAsync(User user)
        {
            return await _db.Query("Users").InsertGetIdAsync<int>(new
            {
                user.PlayerId,
                user.NickName,
                user.Email
            });
        }

        public async Task<int> UpdateUserAsync(User user)
        {
            return await _db.Query("Users").Where("Uid", user.Uid).UpdateAsync(new
            {
                user.PlayerId,
                user.NickName,
                user.Email
            });
        }

        public async Task<int> DeleteUserAsync(int uid)
        {
            return await _db.Query("Users").Where("Uid", uid).DeleteAsync();
        }
    }
}

 

5. Service

  • 역할: 비즈니스 로직을 담당하며, Repository가 제공하는 메서드를 활용하여 로직을 구성합니다. 예를 들어 트랜잭션 관리, 복잡한 비즈니스 규칙 등을 처리합니다.
// Interfaces/IUserService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Server.Models;

namespace Server.Interfaces
{
    public interface IUserService
    {
        Task<IEnumerable<User>> GetAllUsersAsync();
        Task<User> GetUserByIdAsync(int uid);
        Task<int> CreateUserAsync(User user);
        Task<int> UpdateUserAsync(User user);
        Task<int> DeleteUserAsync(int uid);
    }
}

// Services/UserService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Server.Interfaces;
using Server.Models;

namespace Server.Services
{
    public class UserService : IUserService
    {
        private readonly IUserRepository _userRepository;

        public UserService(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }

        public async Task<IEnumerable<User>> GetAllUsersAsync()
        {
            return await _userRepository.GetAllUsersAsync();
        }

        public async Task<User> GetUserByIdAsync(int uid)
        {
            return await _userRepository.GetUserByIdAsync(uid);
        }

        public async Task<int> CreateUserAsync(User user)
        {
            return await _userRepository.AddUserAsync(user);
        }

        public async Task<int> UpdateUserAsync(User user)
        {
            return await _userRepository.UpdateUserAsync(user);
        }

        public async Task<int> DeleteUserAsync(int uid)
        {
            return await _userRepository.DeleteUserAsync(uid);
        }
    }
}

 

 

예시를 ASP.NET core로 들어서, 이어서 종속성 주입에 대한 부분도 간단하게 살펴보자면..

Program.cs 파일에서 서비스와 리포지토리를 DI 컨테이너에 등록해줘야 한다.

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SqlKata.Compilers;
using SqlKata.Execution;
using MySql.Data.MySqlClient;
using Server.Interfaces;
using Server.Repositories;
using Server.Services;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

// Add QueryFactory to DI
builder.Services.AddTransient<QueryFactory>(provider =>
{
    var connection = new MySqlConnection(builder.Configuration.GetConnectionString("DefaultConnection"));
    connection.Open(); // MySQL 연결을 오픈합니다.
    var compiler = new MySqlCompiler();
    return new QueryFactory(connection, compiler);
});

// Register repositories and services
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<DatabaseService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseRouting();

app.UseAuthorization();

app.MapControllers();

// Initialize Database
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var dbService = services.GetRequiredService<DatabaseService>();
    await dbService.InitializeDatabaseAsync();
}

app.Run();

 

사실 위 코드는 GPT가 구성해주어서 약간 이상한 부분이 있을수도 있다.

추후에 내 프로젝트 코드로 업데이트 하겠다!

 

+) 내가 이해한대로 다이어그램도 대충 그려서 추가하겠다!

 

[간단 요약]

1. Model

  • 애플리케이션의 데이터 구조를 정의합니다.
  • 데이터베이스 테이블에 매핑되는 클래스들을 포함합니다.

2. View

  • 사용자 인터페이스를 담당하며, 데이터의 시각적 표현을 정의합니다.
  • API 서버에서는 주로 JSON 응답을 반환합니다.

3. Controller

  • 클라이언트로부터 요청을 받아 적절한 Service를 호출하여 응답을 반환합니다.
  • HTTP 메서드(GET, POST, PUT, DELETE)에 따라 작업을 수행합니다.

4. Repository

  • 데이터베이스와의 상호작용을 담당하며, CRUD 연산을 처리합니다.
  • 인터페이스와 클래스로 구성됩니다.

5. Service

  • 비즈니스 로직을 담당하며, Repository가 제공하는 메서드를 활용하여 로직을 구성합니다.
  • 예를 들어 트랜잭션 관리, 복잡한 비즈니스 규칙 등을 처리합니다.

6. 종속성 주입 (Dependency Injection)

  • 애플리케이션 시작 시 필요한 서비스와 리포지토리를 DI 컨테이너에 등록하여, 각 구성 요소에서 의존성을 주입받아 사용할 수 있게 합니다.

'컴공의 일상 > C#' 카테고리의 다른 글

[Blazor] 컴포넌트 소개 및 활용  (0) 2024.07.15
[Redis] Redis란? 설치 및 시작과 기본 명령어  (0) 2024.07.10
C# [Required] vs required 차이점  (1) 2024.04.22
namespace 란?  (0) 2024.04.17
C# 공부 시작!  (0) 2024.04.11