본문 바로가기
기타

[ASP.NET Core/APOD] View와 RedirectToAction 메서드에 대한 고찰

by 항붕쿤 2023. 4. 11.

VIew 메서드와 RedirectToAction 메서드.. 언뜻 보기에는 두 메서드 모두 HTTP 응답을 생성하는 메서드로 같아보일 수 있으며 실제로 서로가 서로를 대체할 수도 있다.
하지만 두 메서드는 자신들의 확고한 사용처가 존재하며 그 사용처 이외의 곳에 사용할 경우 여러 번거로움이 뒤따른다.

먼저 View 메서드는 컨트롤러에서 모델을 반환하도록한다. 즉, 모델을 뷰에 전달하여 뷰에서 데이터를 보여주고 웹 페이지를 생성하며, View 메서드의 인수로 전달된 모델은 뷰에서 사용되는 데이터를 제공하는 데 사용됩니다.

RedirectToAction 메서드는 일반적으로 다른 동작(액션 메서드)으로 리디렉션할 때 사용된다. 새로운 URL로 이동하며 브라우저가 해당 URL에 대한 새로운 요청을 보내므로 서버는 새로운 HTTP 응답을 생성한다.

예를 들어

public async Task<IActionResult> Delete_Comment(int id, int postid, string password)
{

    var commentModel = await _context.CommentModel.FindAsync(id);
    if (commentModel != null) {
        if(password == commentModel.Password) {
            _context.CommentModel.Remove(commentModel);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Details), new { id = postid });
        }
        else
        {
            TempData["msg"] = "<script>alert('비밀번호가 일치하지 않습니다.');</script>";
            return RedirectToAction(nameof(Details), new { id = postid });
        }
    }
    TempData["msg"] = "<script>alert('비밀번호가 일치하지 않습니다.');</script>";
    return RedirectToAction(nameof(Details), new { id = postid });
}

다음과 같은 코드가 있다고 가정하고 Details 뷰가 받는 모델 형식이 @model APOD.Controllers.APODAndCommentViewModel 라고 생각해보자
이 코드의 경우 매개변수로 받아온 password가 commentModel.Password와 같으면 commentModel을 삭제하고 Details 메서드를 실행시켜 Details 뷰로 리디렉션 시킨다.

위의 경우는 굳이 Details 뷰에서 요구하는 모델을 가져와서 전달할 필요가 없기 때문에 RedirectToAction 메서드를 쓴다.
굳이 View를 쓰고싶다면 다음과 같이 수정하면 된다.

public async Task<IActionResult> Delete_Comment(int id, int postid, string password)
{
    var apodModel = await _context.APODModel.FirstOrDefaultAsync(m => m.Id == postid);
    var commentModels = _context.CommentModel.Where(c => c.PostId == postid).OrderByDescending(d => d.Date).ToList();
    var viewModel = new APODAndCommentViewModel
    {
        APOD = apodModel,
        New_Comment = new CommentModel(),
        Comment = commentModels,
    };

    var commentModel = await _context.CommentModel.FindAsync(id);
    if (commentModel != null) {
        if(password == commentModel.Password) {
            _context.CommentModel.Remove(commentModel);
            await _context.SaveChangesAsync();
            return View(nameof(Details), viewModel);
        }
        else
        {
            TempData["msg"] = "<script>alert('비밀번호가 일치하지 않습니다.');</script>";
            return View(nameof(Details), viewModel);
        }
    }
    ModelState.AddModelError("", "댓글이 존재하지 않습니다.");
    return View(nameof(Details), viewModel);
}

하지만 이렇게 코딩을 할 경우 코드의 길이가 길어지게되고
처리속도도 느려지게된다. 이유는 RedirectToAction이 브라우저로부터 새로운 요청을 만들고, 새로운 요청에 대한 응답을 생성하는(단순히 브라우저에게 다른 URL로 이동하도록 요청하는) 반면, View는 뷰를 렌더링하고 HTML을 생성하는 작업이 필요하기 때문이다. 따라서 View는 다른 페이지로 이동할 때 RedirectToAction보다 추가적인 처리 시간이 필요하다.
(처리속도 차이는 미미하긴하다.)

아 참고로 View 메서드의 매개변수로 모델이 전달되지 않을경우, 뷰 파일에서 선언된 모델 타입과 일치하는 모델을 View 메서드가 자동으로 생성해서 전달한다.
근데 그 값은 모두 기본값으로 초기화되어 있다.(int라면 0, string이라면 null) 때문에 해당 뷰에서 모델을 사용하는 경우 기본값으로 초기화된 모델 인스턴스를 사용하게 된다.

그러나 만약에 View 메서드에서 모델을 전달하지 않으면서 뷰에서 모델을 사용하지 않는다면 컴파일 오류가 발생하지 않는다. 다만 이 경우에는 해당 뷰에서 모델을 사용할 수 없게 됩니다.

그러니까 return View(nameof(Details)) 메서드를 썼고 Details 뷰 파일에서 선언한 모델이 @model APOD.Controllers.APODAndCommentViewModel인데 이 모델에 내재돼 있는 데이터를 사용하려고만 안한다면 오류는 안난다는 것이다.

더 쉽게 얘기하면

<form asp-action="Details" asp-route-id="@Model.APOD.Id">
	<div asp-validation-summary="ModelOnly" class="text-danger"></div>

	<div class="card">
		<div class="card-body container">
			<div class="row mb-3">
				<div class="col-1">
					<input asp-for="New_Comment.Author" class="form-control" placeholder="작성자" />
					<span asp-validation-for="New_Comment.Author" class="text-danger"></span>
				</div>
				<div class="col-1">
					<input asp-for="New_Comment.Password" class="form-control" placeholder="비밀번호" />
					<span asp-validation-for="New_Comment.Password" class="text-danger"></span>
				</div>

			</div>
			<div class="mb-3">
				<textarea asp-for="New_Comment.Contents" class="form-control" placeholder="댓글 입력.." rows="3"></textarea>
			</div>
			<div class="mb-3">
				<span asp-validation-for="New_Comment.Contents" class="text-danger"></span>
			</div>
			<input type="submit" value="댓글 등록" class="btn btn-primary" />

		</div>
	</div>
</form>

이 같은 모델의 폼을 이용한 데이터 전달은 가능하지만

<div>
	<h4>APOD</h4>
	<hr />
	<dl class="row">
		<dt class="col-sm-2">
			@Html.DisplayNameFor(model => model.APOD.Title)
		</dt>
		<dd class="col-sm-10">
			@Html.DisplayFor(model => model.APOD.Title)
		</dd>
		<dt class="col-sm-2">
			@Html.DisplayNameFor(model => model.APOD.Date)
		</dt>
		<dd class="col-sm-10">
			@Html.DisplayFor(model => model.APOD.Date)
		</dd>
		<dt class="col-sm-2">
			@Html.DisplayNameFor(model => model.APOD.Explanation)
		</dt>
		<dd class="col-sm-10">
			@Html.DisplayFor(model => model.APOD.Explanation)
		</dd>
		<dt class="col-sm-2">
			@Html.DisplayNameFor(model => model.APOD.Hdurl)
		</dt>
		<dd class="col-sm-10">
			<img src="@Html.DisplayFor(model => model.APOD.Hdurl)"
				 style="max-width: 400px; height: auto;" alt="image" />
		</dd>
	</dl>
</div>

이러한 모델에 내재된 데이터를 이용하려하면 오류가 난다는 것.