ASP.NET MVC 4에서 사용자 지정 오류 페이지를 작동시키는 방법


247

500, 404 및 403에 대해 사용자 정의 오류 페이지를 표시하고 싶습니다. 수행 한 작업은 다음과 같습니다.

  1. web.config에서 다음과 같이 사용자 정의 오류를 활성화했습니다.

    <customErrors mode="On" 
                  defaultRedirect="~/Views/Shared/Error.cshtml">
    
        <error statusCode="403" 
               redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />
    
        <error statusCode="404" 
               redirect="~/Views/Shared/FileNotFound.cshtml" />
    
    </customErrors>
    
  2. 다음과 같이 클래스 HandleErrorAttribute에서 전역 액션 필터로 등록 되었습니다 FilterConfig.

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomHandleErrorAttribute());
        filters.Add(new AuthorizeAttribute());
    }
    
  3. 위의 각 메시지에 대한 사용자 정의 오류 페이지를 작성했습니다. 500의 기본 값은 이미 기본적으로 사용 가능합니다.

  4. 각 사용자 정의 오류 페이지보기에서 페이지의 모델이 System.Web.Mvc.HandleErrorInfo

500의 경우 사용자 정의 오류 페이지가 표시됩니다. 다른 사람들에게는 그렇지 않습니다.

내가 놓친 것이 있습니까?

클래스 의 OnException메소드 에서 코드를 읽을 때 사용자 정의 오류가 표시되는 것은 아니며 HandleErrorAttribute500 개만 처리하는 것 같습니다.

다른 오류를 처리하려면 어떻게해야합니까?


21
이 설정에서 이상한 점은 컨트롤러 작업이 아닌보기로 리디렉션한다는 것입니다. 예를 들어 누가 이러한 뷰를 렌더링하고 모델을 전달해야합니까? 그냥 생각해
Oliver

2
여기에있는 대부분의 답변은 모든 경우를 처리하지 않거나 웹 서버가 "잘못된"방식으로 응답합니다 (예 : 오류 응답을 반환하는 대신 오류 페이지로 리디렉션). 웹 서버가 예상하는 방식으로 서버가 응답하는 것에 관심이 있다면 benfoster.io/blog/aspnet-mvc-custom-error-pages에 대한 자세한 기사가 있습니다 . 여기에 대한 답변만큼 간단하지는 않으므로 경고를 쉽게 받으려면 아래 중 하나를 사용하십시오.
rdans

1
다음은 asp.net 오류 처리에 대한 다양한 기술에 대한 또 다른 훌륭한 기사입니다. dusted.codes/…
Godsayah

답변:


352

내 현재 설정 (MVC3에서, 여전히 적용된다고 생각합니다)은에 의존 ErrorController하므로 다음을 사용합니다.

<system.web>
    <customErrors mode="On" defaultRedirect="~/Error">
      <error redirect="~/Error/NotFound" statusCode="404" />
    </customErrors>
</system.web>

컨트롤러에는 다음이 포함됩니다.

public class ErrorController : Controller
{
    public ViewResult Index()
    {
        return View("Error");
    }
    public ViewResult NotFound()
    {
        Response.StatusCode = 404;  //you may want to set this to 200
        return View("NotFound");
    }
}

뷰는 구현 방식과 동일합니다. 그래도 응용 프로그램이 디버그 모드에있는 경우 스택 추적 및 오류 정보를 표시하기 위해 약간의 논리를 추가하는 경향이 있습니다. 따라서 Error.cshtml은 다음과 같습니다.

@model System.Web.Mvc.HandleErrorInfo
@{
    Layout = "_Layout.cshtml";
    ViewBag.Title = "Error";
}
<div class="list-header clearfix">
    <span>Error</span>
</div>
<div class="list-sfs-holder">
    <div class="alert alert-error">
        An unexpected error has occurred. Please contact the system administrator.
    </div>
    @if (Model != null && HttpContext.Current.IsDebuggingEnabled)
    {
        <div>
            <p>
                <b>Exception:</b> @Model.Exception.Message<br />
                <b>Controller:</b> @Model.ControllerName<br />
                <b>Action:</b> @Model.ActionName
            </p>
            <div style="overflow:scroll">
                <pre>
                    @Model.Exception.StackTrace
                </pre>
            </div>
        </div>
    }
</div>

7
이 Pablo에 대해 Global.asax의 Application_Error에 무엇을 넣어야합니까?
Alicia

12
컨트롤러의 코드가 내 경험으로는 실행되지 않는 것 같습니다. MVC4-다른 컨트롤러에서 System.Exception을 발생 시키면 ErrorController를 통하지 않고 Error.cshtml 파일이 렌더링됩니다. 다른 사람이 이것을 경험?
Nilzor

53
도움이되었지만 더 많은 맥락이 필요한 다른 사람들에게; <customErrors> 태그는 web.config의 <system.web>에 들어갑니다.
gooberverse

7
다른 사람에 대한 업데이트 - 내가했기 때문에 분명히 내 문제는 무슨 일이 벌어지고 redirectMode="ResponseRewrite"CustomerErrors요소
KyleMit

42
하나님의 사랑이 //you may want to set this to 200코드에서 말하는 주석을 무시하십시오 . 그거 하지마!
치매

40

파블로 솔루션을 수행했으며 항상 오류가 발생했습니다 (MVC4)

'오류'보기 또는 해당 마스터를 찾을 수 없거나 검색된 위치를 지원하는보기 엔진이 없습니다.

이를 제거하려면 줄을 제거하십시오

 filters.Add(new HandleErrorAttribute());

FilterConfig.cs에서


나는 이것을 해결하기 위해 모든 곳을 보았다. 이것은 마침내 답을 얻었습니다. 나는 그것이 왜 그 일을했는지 ​​알았지 만 나를 도대체 다른 사람들이 말한 것처럼 크게 생각하지 않고는 할 수 없었습니다. 이것을 지적 해 주셔서 감사합니다. 전설!
Adam

이것은 하나의 옵션이며 오류 컨트롤러가 제대로 작동합니다. 그러나 FilterConfig.cs에 필터를 등록하면 공유 및 원래 컨트롤러의보기 폴더에서 Error.cshtml을 찾습니다. Error.cshtml을 사용자 지정 ErrorController 이외의 다른 것으로 변경하면 작동합니다. 그러나이 등록을 추가 할 수있는 곳이 있으며 global.asax.cs입니다. global.asax.cs의 RegisterGlobalFilters (GlobalFilterCollection filters) 함수에 언급 된 행을 추가하고 FilterConfig.cs에서 제거하면 작동합니다.
isaolmez

필터 등록 순서와 관련이 있다고 생각합니다. 오류 컨트롤러를 유지하고 필터 등록을 global.asax.cs로 이동하십시오. public static void RegisterGlobalFilters (GlobalFilterCollection 필터) {filters.Add (new HandleErrorAttribute ()); }
isaolmez

24

게시 된 다른 솔루션보다 코딩이 덜 필요한 작업을 수행합니다.

먼저 web.config에 다음이 있습니다.

<customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
   <error redirect="~/ErrorPage/Oops/404" statusCode="404" />
   <error redirect="~/ErrorPage/Oops/500" statusCode="500" />
</customErrors>

컨트롤러 (/Controllers/ErrorPageController.cs)에는 다음이 포함됩니다.

public class ErrorPageController : Controller
{
    public ActionResult Oops(int id)
    {
        Response.StatusCode = id;

        return View();
    }
}

마지막으로보기에는 다음과 같은 내용이 포함되어 있습니다 (단순화를 위해 아래로 분류되었지만 오염 될 수 있음).

@{ ViewBag.Title = "Oops! Error Encountered"; }

<section id="Page">
  <div class="col-xs-12 well">
    <table cellspacing="5" cellpadding="3" style="background-color:#fff;width:100%;" class="table-responsive">
      <tbody>
        <tr>
          <td valign="top" align="left" id="tableProps">
            <img width="25" height="33" src="~/Images/PageError.gif" id="pagerrorImg">
          </td>
          <td width="360" valign="middle" align="left" id="tableProps2">
            <h1 style="COLOR: black; FONT: 13pt/15pt verdana" id="errortype"><span id="errorText">@Response.Status</span></h1>
          </td>
        </tr>
        <tr>
          <td width="400" colspan="2" id="tablePropsWidth"><font style="COLOR: black; FONT: 8pt/11pt verdana">Possible causes:</font>
          </td>
        </tr>
        <tr>
          <td width="400" colspan="2" id="tablePropsWidth2">
            <font style="COLOR: black; FONT: 8pt/11pt verdana" id="LID1">
                            <hr>
                            <ul>
                                <li id="list1">
                                    <span class="infotext">
                                        <strong>Baptist explanation: </strong>There
                                        must be sin in your life. Everyone else opened it fine.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Presbyterian explanation: </strong>It's
                                        not God's will for you to open this link.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong> Word of Faith explanation:</strong>
                                        You lack the faith to open this link. Your negative words have prevented
                                        you from realizing this link's fulfillment.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Charismatic explanation: </strong>Thou
                                        art loosed! Be commanded to OPEN!<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Unitarian explanation:</strong> All
                                        links are equal, so if this link doesn't work for you, feel free to
                                        experiment with other links that might bring you joy and fulfillment.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Buddhist explanation:</strong> .........................<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Episcopalian explanation:</strong>
                                        Are you saying you have something against homosexuals?<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Christian Science explanation: </strong>There
                                        really is no link.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Atheist explanation: </strong>The only
                                        reason you think this link exists is because you needed to invent it.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Church counselor's explanation:</strong>
                                        And what did you feel when the link would not open?
                                    </span>
                                </li>
                            </ul>
                            <p>
                                <br>
                            </p>
                            <h2 style="font:8pt/11pt verdana; color:black" id="ietext">
                                <img width="16" height="16" align="top" src="~/Images/Search.gif">
                                HTTP @Response.StatusCode - @Response.StatusDescription <br>
                            </h2>
                        </font>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</section>

그것은 그렇게 간단합니다. 더 자세한 오류 정보를 제공하기 위해 쉽게 확장 할 수 있지만 ELMAH 는 나를 위해 처리하며 statusCode 및 statusDescription 만 있으면됩니다.


"~ / ErrorPage / Oops / 404"의 .config 파일에서 리디렉션이 아마도 "~ / ErrorPage / Oops? 404"여야한다고 생각합니까? 적어도 그것은 나를 위해 일한 것입니다. 아마도 그것은 라우팅에 달려 있습니다.
Josh Sutterfield

IIS에서 발생하는 오류를 시뮬레이션하는 방법 수 그것은 500 ASP.Net MVC에서해야 할 일 504 - 5 코드를 내가 내 사용자 지정 오류 페이지를 테스트 할 수 있도록 IIS에서 예외를 시뮬레이션
언 브레이커 블

12

여기에 여러 단계가 뒤섞인 것 같습니다. 내가 한 일을 처음부터 전달하겠습니다.

  1. ErrorPage컨트롤러 만들기

    public class ErrorPageController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    
        public ActionResult Oops(int id)
        {
            Response.StatusCode = id;
            return View();
        }
    }
    
  2. 이 두 조치에 대한보기를 추가하십시오 (오른쪽 클릭->보기 추가). 이들은 ErrorPage라는 폴더에 나타납니다.

  3. 내부를 App_Start열고 FilterConfig.cs오류 처리 필터를 주석 처리하십시오.

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        // Remove this filter because we want to handle errors ourselves via the ErrorPage controller
        //filters.Add(new HandleErrorAttribute());
    }
    
  4. web.config에서 다음 <customerErrors>항목을 추가하십시오.System.Web

    <customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
        <error redirect="~/ErrorPage/Oops/404" statusCode="404" />
        <error redirect="~/ErrorPage/Oops/500" statusCode="500" />
    </customErrors>
    
  5. 물론 테스트하십시오. 코드에 처리되지 않은 예외를 던져서 ID가 500 인 페이지로 이동 한 다음 존재하지 않는 페이지의 URL을 사용하여 404를 확인하십시오.


이 오류가 발생했습니다. An exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been terminated.코드에서 가져온 모든 것이 web.config 파일에 있으며 추가 <error redirect = "~/ControllerName/ActionName" statusCode="404"/>되었으며 정상적으로 작동했습니다.) 나머지 코드는 @Pablo의 답변에서 나왔습니다. 내가 MVC (5) 사용하고 엔티티 프레임 워크 6. 나는 제거하지 않은 filters.Add(new HandleErrorAttribute())에서FilterConfig.cs
sumedha

IIS에서 발생하는 오류를 시뮬레이션하는 방법 수 그것은 500 ASP.Net MVC에서해야 할 일 504 - 5 코드를 내가 내 사용자 지정 오류 페이지를 테스트 할 수 있도록 IIS에서 예외를 시뮬레이션
언 브레이커 블

또한 처리되지 않은 예외를 발생시키는 방법 (5 단계). 코딩을 처음 접하십시오.
깨지지 않는

여전히 나를 위해 작동하지 않습니까? 라우팅은 어떻습니까? Routing for Error 페이지도 추가해야합니까? 내가 페이지에 충돌하는 경우 : 로컬 호스트 : 84 / Enforcer를 / ㅋ ㅋ : I가 리디렉션 로컬 호스트 : 84 / Enforcer의 / Enforcer의 / 오류 / NOTFOUND aspxerrorpath = / ...? Asp.NET에서 제공하는 표준 오류 페이지와 같은 오류 페이지를 찾습니다. 어떤 아이디어?
Radek Strugalski

webconfig의 customerrors 요소가이를 처리해야합니다. (프로젝트가 만든) 기본 경로 코드가 제대로 작동합니다.
VictorySaber

11

Global.asax.cs 파일을 사용하는 것이 좋습니다.

 protected void Application_Error(Object sender, EventArgs e)
{
    var exception = Server.GetLastError();
    if (exception is HttpUnhandledException)
    {
        Server.Transfer("~/Error.aspx");
    }
    if (exception != null)
    {
        Server.Transfer("~/Error.aspx");
    }
    try
    {
        // This is to stop a problem where we were seeing "gibberish" in the
        // chrome and firefox browsers
        HttpApplication app = sender as HttpApplication;
        app.Response.Filter = null;
    }
    catch
    {
    }
}

1
MVC에서 Server.Transfer () 할 수 있다고 생각하지 않았습니다. OP에 혼합 사이트가 있다고 생각하십니까?

1
왜 mvc에서 Application_Error를 사용해야합니까? 리디렉션 URL 옵션이있는 [handleerror] 속성과 같은 옵션이 있습니다. application_error에 특별한 이점이 있습니까?
Kurkula

우리는 MVC에서 HandleErrorAttribute를 사용해야하고 OnException 메소드를 재정의함으로써 훨씬 더 나은 방식으로 처리 할 수 ​​있습니다
Kumar Lachhani

7

maxspan이 게시 한 답변을 바탕으로 모든 작업 부품을 보여주는 최소 샘플 프로젝트를 GitHub 에 모았습니다 .

기본적으로, 우리 는 예외를 가로 채기 Application_Error위해 global.asax.cs 에 메소드를 추가하고 우리에게 커스텀 에러 페이지 로 리다이렉트 (또는 더 정확하게 요청을 전송 ) 할 기회를줍니다 .

    protected void Application_Error(Object sender, EventArgs e)
    {
        // See http://stackoverflow.com/questions/13905164/how-to-make-custom-error-pages-work-in-asp-net-mvc-4
        // for additional context on use of this technique

        var exception = Server.GetLastError();
        if (exception != null)
        {
            // This would be a good place to log any relevant details about the exception.
            // Since we are going to pass exception information to our error page via querystring,
            // it will only be practical to issue a short message. Further detail would have to be logged somewhere.

            // This will invoke our error page, passing the exception message via querystring parameter
            // Note that we chose to use Server.TransferRequest, which is only supported in IIS 7 and above.
            // As an alternative, Response.Redirect could be used instead.
            // Server.Transfer does not work (see https://support.microsoft.com/en-us/kb/320439 )
            Server.TransferRequest("~/Error?Message=" + exception.Message);
        }

    }

오류 컨트롤러 :

/// <summary>
/// This controller exists to provide the error page
/// </summary>
public class ErrorController : Controller
{
    /// <summary>
    /// This action represents the error page
    /// </summary>
    /// <param name="Message">Error message to be displayed (provided via querystring parameter - a design choice)</param>
    /// <returns></returns>
    public ActionResult Index(string Message)
    {
        // We choose to use the ViewBag to communicate the error message to the view
        ViewBag.Message = Message;
        return View();
    }

}

오류 페이지보기 :

<!DOCTYPE html>

<html>
<head>
    <title>Error</title>
</head>
<body>

    <h2>My Error</h2>
    <p>@ViewBag.Message</p>
</body>
</html>

FilterConfig.csfilters.Add(new HandleErrorAttribute()) 에서 비활성화 / 제거 이외의 다른 것은 없습니다

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //filters.Add(new HandleErrorAttribute()); // <== disable/remove
    }
}

구현이 매우 간단하지만이 방법에서 볼 수있는 한 가지 단점은 querystring을 사용하여 예외 정보를 대상 오류 페이지에 전달하는 것입니다.


3

모든 것이 설정되었지만 로컬 개발 서버에서 모든 것이 제대로 작동했지만 스테이징 서버에서 상태 코드 500에 대한 적절한 오류 페이지를 여전히 볼 수 없었습니다.

나는 발견 이 블로그 게시물 나를 도와 릭 Strahl에서합니다.

Response.TrySkipIisCustomErrors = true;사용자 지정 오류 처리 코드 를 추가해야했습니다 .


@ Shaun314 그 코드를 어디에 넣었습니까? 요청을 처리하는 작업에서 해당 블로그 게시물에서 예제를 볼 수 있습니다.
DCShannon

2

여기 내 해결책이 있습니다. 사용 [ExportModelStateToTempData] / [ImportModelStateFromTempData]는 내 의견에 불편하다.

~ / Views / Home / Error.cshtml :

@{
    ViewBag.Title = "Error";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Error</h2>
<hr/>

<div style="min-height: 400px;">

    @Html.ValidationMessage("Error")

    <br />
    <br />

    <button onclick="Error_goBack()" class="k-button">Go Back</button>
    <script>
        function Error_goBack() {
            window.history.back()
        }
    </script>

</div>

~ / Controllers / HomeController.sc :

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Error()
    {
        return this.View();
    }

    ...
}

~ / Controllers / BaseController.sc :

public class BaseController : Controller
{
    public BaseController() { }

    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is ViewResult)
        {
            if (filterContext.Controller.TempData.ContainsKey("Error"))
            {
                var modelState = filterContext.Controller.TempData["Error"] as ModelState;
                filterContext.Controller.ViewData.ModelState.Merge(new ModelStateDictionary() { new KeyValuePair<string, ModelState>("Error", modelState) });
                filterContext.Controller.TempData.Remove("Error");
            }
        }
        if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult))
        {
            if (filterContext.Controller.ViewData.ModelState.ContainsKey("Error"))
            {
                filterContext.Controller.TempData["Error"] = filterContext.Controller.ViewData.ModelState["Error"];
            }
        }

        base.OnActionExecuted(filterContext);
    }
}

~ / Controllers / MyController.sc :

public class MyController : BaseController
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Details(int id)
    {
        if (id != 5)
        {
            ModelState.AddModelError("Error", "Specified row does not exist.");
            return RedirectToAction("Error", "Home");
        }
        else
        {
            return View("Specified row exists.");
        }
    }
}

나는 당신에게 성공적인 프로젝트를 기원합니다 ;-)


2

global.cs를 해킹하지 않고 HandleErrorAttribute로 엉망으로 만들거나 Response.TrySkipIisCustomErrors를 수행하거나 Application_Error를 연결하는 등의 작업을 수행하지 않고도 오류가 올바르게 발생할 수 있습니다.

system.web에서 (보통, 온 / 오프)

<customErrors mode="On">
  <error redirect="/error/401" statusCode="401" />
  <error redirect="/error/500" statusCode="500" />
</customErrors>

그리고 system.webServer에서

<httpErrors existingResponse="PassThrough" />

이제 상황이 예상대로 작동하고 ErrorController를 사용하여 필요한 것을 표시 할 수 있습니다.


IIS에서 발생하는 오류를 시뮬레이션하는 방법 수 그것은 500 ASP.Net MVC에서해야 할 일 504 - 5 코드를 내가 내 사용자 지정 오류 페이지를 테스트 할 수 있도록 IIS에서 예외를 시뮬레이션
언 브레이커 블

@Unbreakable은 일시적으로 코드를 변경하여 예외를 발생시킵니다.
theycallmemorty

나에게 차이를 만들지 않았다. 예외 또는 404 찾을 수 없음 오류가 발생하면 사용자 지정 오류 페이지로 이동하지 않습니다.
pnizzle

0

내가 파티에 늦게 온 것 같지만 이것도 더 잘 확인해야합니다.

따라서 system.webreturn HttpNotFound ()와 같은 응용 프로그램 내에서 예외를 캐싱하기 위해

  <system.web>
    <customErrors mode="RemoteOnly">
      <error statusCode="404" redirect="/page-not-found" />
      <error statusCode="500" redirect="/internal-server-error" />
    </customErrors>
  </system.web>

그리고 system.webServerIIS에 의해 잡히고 asp.net 프레임 워크로 향하지 않은 오류를 잡기 위해

 <system.webServer>
    <httpErrors errorMode="DetailedLocalOnly">
      <remove statusCode="404"/>
      <error statusCode="404" path="/page-not-found" responseMode="Redirect"/>
      <remove statusCode="500"/>
      <error statusCode="500" path="/internal-server-error" responseMode="Redirect"/>
  </system.webServer>

마지막에서 당신은 다음 변경 클라이언트 응답에 대해 걱정하는 경우 responseMode="Redirect"responseMode="File"이 하나가 200 응답 코드와 함께 친절한 페이지를 표시하기 때문에, 정적 HTML 파일을 제공합니다.


0

web.config에서 아래처럼 system.webserver 태그 아래에 이것을 추가하십시오.

<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
  <remove statusCode="404"/>
  <remove statusCode="500"/>
  <error statusCode="404" responseMode="ExecuteURL" path="/Error/NotFound"/>
  <error statusCode="500" responseMode="ExecuteURL"path="/Error/ErrorPage"/>
</httpErrors>

컨트롤러를 다음과 같이 추가하십시오.

public class ErrorController : Controller
{
    //
    // GET: /Error/
    [GET("/Error/NotFound")]
    public ActionResult NotFound()
    {
        Response.StatusCode = 404;

        return View();
    }

    [GET("/Error/ErrorPage")]
    public ActionResult ErrorPage()
    {
        Response.StatusCode = 500;

        return View();
    }
}

존경받는 견해를 추가하면 분명히 모든 사람에게 맞는 것 같습니다.

내가 찾은이 솔루션 : Neptune Century

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.