2020. 9. 17. 15:30

전편에서 했던 "API 결과 리턴 공통화"는 좀 급하게 만들다 보니 빠진 것이 있는데......

가장 중요하다고 할 수 있는 '결과 처리 클래스'가 공통화가 덜돼서 최종 결과를 출력하는 'ApiResultReady.ToResult()'를 호출할 때 출력할 객체를 따로 지정해야 했습니다.


이번엔 이 불편함을 고쳐봅시다.




1. 'ApiResultBaseModel' 수정
'ApiResultReady'에서 하던 성공 실패 여부를 처리하기 위한 기능을

'ApiResultBaseModel'으로 옮여야 합니다.


성공 문자를 저장해두고 이것과 비교하는 함수를 추가합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/// <summary>
/// API 결과 공통 베이스.
/// </summary>
public class ApiResultBaseModel
{
    /// <summary>
    /// 실패시 전달한 코드
    /// 0 : 성공.
    /// 다른 값은 모두 실패
    /// </summary>
    public string InfoCode { get; set; }
    /// <summary>
    /// 전달할 메시지
    /// </summary>
    public string Message { get; set; }
 
    /// <summary>
    /// 성공했을때 문자
    /// </summary>
    private string SuccessString;
 
    /// <summary>
    /// 기본 생성.
    /// InfoCode가 "0"로 초기화됨
    /// </summary>
    public ApiResultBaseModel()
    {
        this.SuccessString = ApiResultType.None.GetHashCode().ToString();
 
        this.Reset();
    }
 
    /// <summary>
    /// 인포코드와 메시지를 넣고 생성
    /// </summary>
    /// <param name="sInfoCode"></param>
    /// <param name="sMessage"></param>
    public ApiResultBaseModel(string sInfoCode, string sMessage)
    {
        this.SuccessString = ApiResultType.None.GetHashCode().ToString();
 
        this.InfoCode = sInfoCode;
        this.Message = sMessage;
    }
 
    /// <summary>
    /// 성공으로 초기화한다.
    /// </summary>
    public void Reset()
    {
        this.InfoCode = this.SuccessString;
        this.Message = string.Empty;
    }
 
    /// <summary>
    /// 타입 세팅
    /// </summary>
    /// <param name="typeApiResult"></param>
    public void TypeSet(ApiResultType typeApiResult)
    {
        this.InfoCode = typeApiResult.GetHashCode().ToString();
    }
 
    /// <summary>
    /// InfoCode값이 성공값인지 여부
    /// </summary>
    /// <returns></returns>
    public bool IsSuccess()
    {
        bool bReturn = false;
 
        if(this.InfoCode == this.SuccessString)
        {//성공문자열이다.
            bReturn = true;
        }
 
        return bReturn;
 
    }
}
cs



20번 라인 : 생성자에서 여기다 성공했을 때 사용할 문자열을 초기화해줍니다.


49번 라인 : 초기 상태로 초기화해줍니다.


68번 라인 : 'InfoCode'값이 성공 문자열과 같은지 여부를 리턴합니다.

이 함수를 통해 지금 가지고 있는 'ApiResultBaseModel'의 내용을 판단합니다.



2. 'ApiResultReady' 수정

전편에서 만든 'ApiResultReady'를 좀 수정해야 합니다.


2-1. 스테이터스 코드 제거

전 버전에서는 '스테이터스 코드'를 사용했는데....

써보니까 이게 많이 불편합니다 ㅎㅎㅎ


어차피 서버에서 예상할 수 없는 오류는 결국 '500' 뿐이고

그렇다면 결국 값을 가지고 있는 건 의미가 없습니다.


결국 성공과 실패를 전달해주면 됩니다.


1
2
3
4
5
6
7
8
9
10
11
/// <summary>
/// 스테이터스 코드
/// </summary>
제거 -> public int StatusCode { get; set; }
 
/// <summary>
/// 성공 여부.
/// 알수 없는 에러가 났을때 처리하기위한 용도.
/// 알수 있는 에러는 이값을을 true로 해둔다.
/// </summary>
public bool Success { get; set; }
cs



이 값은 에러 났는지 여부만 가지고 있으면 됩니다.


이 값이 참(True)이고 'InfoCode'값이 "0"이면 성공한 것으로 취급하게 됩니다.


2-1-1. 'InfoCode'를 판단하는 함수

'ApiResultBaseModel'의 'IsSuccess'함수를 호출하는 함수를 만들어 줍니다.


1
2
3
4
5
6
7
8
/// <summary>
/// InfoCode값이 성공값인지 여부
/// </summary>
/// <returns></returns>
public bool IsSuccess()
{
    return this.ResultObject.IsSuccess();
}
cs




2-2. 인스턴스 저장용 변수

전 버전은 사용할 오브젝트를 마지막에 받아서 사용했습니다.

그러다 보니 'ApiResultReady'가 가지고 있는 변수와 혼동될 때가 있었습니다.

그래서 'ApiResultBaseModel'를 상속받는 인스턴스를 저장해둘 변수가 필요해졌습니다.


1
2
3
4
/// <summary>
/// 전달받은 결과 오브젝트
/// </summary>
public ApiResultBaseModel ResultObject { get; set; }
cs



이제  'ApiResultBaseModel'를 상속받은 인스턴스를 만들었으면 바로 이 변수에 할당하면 됩니다.


예를 들면 아래와 같이 사용합니다.


1
2
3
4
5
//리턴 보조
ApiResultReady rrResult = new ApiResultReady(this);
//리턴용 모델
TestModel01 armResult = new TestModel01();
rrResult.ResultObject = armResult;
cs



생성자에서 'ResultObject'를 'ApiResultBaseModel'로 초기화해주므로 (요 다음에 코드 있음)

'ResultObject'를 초기화하지 않으면 'ApiResultBaseModel'를 사용하게 됩니다.


이렇게 되면 'armResult'는 형 변환 없이 사용 가능하고

'ApiResultBaseModel'의 변수인 'InfoCode'와 'Message'는 

'rrResult'와 'armResult'에서 공유하게 됩니다.



2-3. 'InfoCode', 'Message' 변경
'InfoCode', 'Message'는 이제 'ApiResultReady'가 가지고 있는 것이 아니고 'ResultObject'에 들어있는 값을 쓰도록 해야 합니다.


'InfoCode'를 속성으로 바꾸고 get/set을 아래와 같이 작성합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// <summary>
/// 실패시 전달한 코드
/// 0 : 성공.
/// 다른 값은 모두 실패
/// </summary>
제거 -> public string InfoCode { get; set; }
 
/// <summary>
/// 실패시 전달한 코드
/// 0 : 성공.
/// 다른 값은 모두 실패
/// </summary>
public string InfoCode
{
    get
    {
        return this.ResultObject.InfoCode;
    }
    set
    {
        this.ResultObject.InfoCode = value;
    }
}
cs



'Message'를 속성으로 바꾸고 get/set를 비슷하게 작성합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// <summary>
/// 전달할 메시지
/// </summary>
public string Message
{
    get
    {
        return this.ResultObject.Message;
    }
    set
    {
        this.ResultObject.Message = value;
    }
}
cs



2-4. 'ToResult' 수정
이제 'ResultObject'를 가지고 처리해야 하므로 파라메타가 없는 'ToResult'를 만들고

기존 'ToResult'는 'ResultObject'를 이용하도록 수정합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/// <summary>
/// API끝에서 호출한다.
/// ApiResult를 생성하여 리턴해 준다.
/// </summary>
/// <returns></returns>
public ObjectResult ToResult()
{
    return this.ToResult(this.ResultObject);
}
 
 
/// <summary>
/// API끝에서 호출하여 'ObjectResult'를 생성하여 리턴해 준다.
/// 만들어지는 결과의 ApiResultBaseModel데이터는 this 기준이다.
/// </summary>
/// <param name="objResultData">전달할 모델</param>
/// <returns></returns>
public ObjectResult ToResult(object objResultData)
{
    ObjectResult orReturn = null;
 
    if (null == objResultData)
    {//오브젝트가 없다.
        //없으면 가지고 있는 오브젝트를 자동으로 사용한다.
        objResultData = this.ResultObject;
    }
 
    if (this.Success == true)
    {//성공
        //성공은 전달받은 오브젝트를 준다,
        orReturn = this.ThisCB.StatusCode(StatusCodes.Status200OK, objResultData);
    }
    else
    {//실패
        //실패는 500 에러를 기본으로 전달해야 한다.
        ApiResultFailModel afm 
            = new ApiResultFailModel(
                ((ApiResultBaseModel)objResultData).InfoCode
                , ((ApiResultBaseModel)objResultData).Message);
 
        //여기에 들어왔다는건 예측 가능한 오류가 났다는 의미다.
        //예측가능한 오류는 200으로 바꿔준다.
        orReturn = this.ThisCB.StatusCode(StatusCodes.Status200OK, afm);
        //여기서 예측가능한 오류를 200으로 바꾸지 않으려면 이 코드를 사용한다.
        //orReturn = this.ThisCB.StatusCode(this.StatusCode, afm);
    }
 
    return orReturn;
}
cs



3. 사용하기
사용하는 방법은 이전 버전과 비슷합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[HttpGet]
public ActionResult<TestModel01> Test01(int nData, string sData)
{
    //리턴 보조
    ApiResultReady rrResult = new ApiResultReady(this);
    //리턴용 모델
    TestModel01 armResult = new TestModel01();
    rrResult.ResultObject = armResult;
 
    if (0 <= nData)
    {//양수다.
        armResult.nTest = nData;
        armResult.sTest = sData;
    }
 
    if (false == rrResult.IsSuccess())
    {
        rrResult.InfoCode = "1";
        rrResult.Message = "'nData'에 음수가 입력되었습니다.";
    }
 
    return rrResult.ToResult(armResult);
}
cs


5번 라인 : 위에서 수정한 'ApiResultReady'의 인스턴스를 생성합니다.


7번 라인 : 이 API에서 사용할 모델의 인스턴스를 생성합니다.

'ApiResultReady'에 바로 생성해서 넣을 수 있긴 합니다.

문제는 그렇게 되면 매번 형 변환을 해서 사용해야 한다는 것이죠.


8번 라인 : 'ApiResultReady'에 7번 라인에서 생성한 모델을 전달합니다.



이제 'rrResult.IsSuccess()'를 호출하여 리턴용 모델이 성공상태인지 확인합니다.


'ApiResultBaseModel'속성에 접근할 때는 

'rrResult'나 'TestModel01'중 어느 쪽에 접근하여도 같은 개체를 사용하게 됩니다.



22번 라인 : 결과를 API 결과 모델로 변환하여 전달하고 있습니다.

에 생성한 'armResult'를 전달합니다.

'ToResult'를 호출할 때 인스턴스를 전달하지 않으면 가지고 있는 인스턴스를 리턴합니다.

인스턴스를 전달하면 전달받은 인스턴스를 기준으로 작업하여 리턴합니다.



마무리
이 버전이 마지막 버전이기를 ㅎㅎㅎ