프로그래밍/Unity C#
[Unity] Tutorial 06 - Intermediate Scripting (Encapsulation)
FireflyStudioX
2020. 1. 20. 21:52
목표
- 캡슐화(Encapsulation)에 대해 알아보자.
캡슐화(Encapsulation)
- 캡슐화란 데이터 구조와 데이터(Attribute)를 다루는 방법(Method)을 결합 시키고, 실제 구현 내용을 외부에서 사용하지 못하도록 은닉하는 것을 말한다.
캡슐화의 목적
오용방지 : 데이터의 잘못된 접근을 막아 데이터를 보호하고 오용을 방지한다.
일관성 : 객체의 내부 구현이 변경되어도, 객체의 사용 방법은 변하지 않는다.
독립성 : 객체의 내부 값이 변경되어도, 다른 객체에 영향을 주지 않는다.
낮은 결합도 : 객체간의 결합도가 떨어진다.
재사용성 : 위 목적을 달성하면, 클래스의 재사용성과 유지보수에 이점을 가져올 수 있다.
오용된 코드
- 예를들어, 인증에 사용되는 Token을 관리한다고 생각해보자.
public class AuthToken
{
public bool IsSuccess; // 로그인 성공 여부
public string AccessToken; // 로그인 성공 시, 리턴되는 토큰
public string RefreshToken; // 로그인 갱신용 토큰
}
예시
- AuthClient라는 서버 통신 결과를 받아 AuthToken을 생성해주는 클래스를 만들어 보았다.
- 여러분이 AuthClient 개발을 해야하고, 누군가에게 AuthToken라는 클래스를 받았다고 생각해보자.
public AuthClient
{
// 로그인 시도
public AuthToken SignIn()
{
// 객체부터 생성하고, 아래에서 값채워넣는 코딩 스타일은
// 시간이 지나서 리펙토링 해보겠다고 위아래로 줄바꾸다가 NullPointException가 발생하는 경우가 많음
AuthToken authToken = new AuthToken();
// TokenApiApiRequest : TokenApiResponse를 반환하는 REST API라고 가정해보자
// TokenApiResponse : REST API 응답 결과, accessToken, refreshToken을 리턴한다고 가정해보자
// 로그인 시도
TokenApiResponse response = TokenApiApiRequest(SIGNIN);
// 서버 응답값이 0이면 성공, 아니면 실패
this.IsSuccess = response.GetResponseCode() == 0 ? true : false;
// 엑세스 토큰도 내가 넣어야 하고
this.AccessToken = response.GetAccessToken();
// 리프레시 토큰도 넣어야 됨
this.RefreshToken = response.GetRefreshToken();
return authToken;
}
// 로그아웃
public AuthToken SignOut()
{
AuthToken authToken = new AuthToken();
// 로그아웃 성공/실패 여부를 리턴하는 함수라고 가정해보자
TokenApiResponse response = TokenApiApiRequest(SIGNOUT);
// 로그아웃
this.IsSuccess = response.GetResponseCode() == 0 ? true : false;
// 로그아웃이 성공인 경우
if(isSuccess)
{
this.AccessToken = null;
}
// 로그 아웃 실패
else
{
// 로그아웃이 실패하면 값이 없는 것이겠지?
return null;
}
return authToken;
}
public AuthToken RefreshToken()
{
AuthToken authToken = new AuthToken();
// 서버 응답 결과 받음
TokenApiResponse response = TokenApiApiRequest(REFRESH);
// 이 경우 서버 응답 결과와 로그인 상태를 맞춰 줘야 겠지?
this.IsSuccess = response.GetResponseCode() == 0 ? true : false;
// 갱신에 성공했으면
if(isSuccess)
{
this.AccessToken = response.GetAccessToken();
}
// 갱신에 실패했으면
else
{
this.AccessToken = null;
}
return authToken;
}
}
다음날 요청사항
- Authorization Code 로그인 방식 추가
- Authorization Code는 int 타입으로 관리
- Authorization Code 로그인인 경우 AccessToken 값은 null로 설정
- Authorization Code가 발급되면 서버 응답으로 6001 리턴
- 이 상태면 AuthToken의 상태는 Pending으로 설정
결과
- 오용방지 :
- 실수로 Authorization Code 로그인 이후, AccessToken에 null을 넣지 않았습니다.
- 실수로 AuthCode와 AuthToken 값을 반대로 삽입
- 일관성 :
- 서버 응답에 따라 로직 변경 필요. (일관성 없음)
- 독립성 :
- 서버 응답값에 로직 의존성 심각
- 낮은 결합도 :
- 독립성이 없으니 결합도는 높음
- 재사용성 :
- WebAuthClinet를 만든다고 할때, 위 AuthToken은 재사용 가능한가?
캡슐화
- 그래서 캡슐화를 진행해 보았다.
public class AuthToken
{
private int responseCode;
private string accessToken;
private int authorizationCode;
private string refreshToken;
public AuthToken(int responseCode, string accessToken, string refreshToken)
{
this.responseCode = responseCode;
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
public AuthToken(int responseCode, int authorizationCode)
{
this.responseCode = responseCode;
this.authorizationCode = authorizationCode;
}
public bool IsSuccess()
{
return responseCode == 0 ? true : false;
}
public bool IsPending()
{
return responseCode == 6001 ? true : false;
}
public int GetResponseCode()
{
return this.responseCode;
}
public string GetAccessToken()
{
return this.accessToken;
}
public string GetRefreshToken()
{
return this.refreshToken;
}
public int GetAuthorizationCode()
{
return this.authorizationCode;
}
public static AuthToken NewRefreshAuthToken(TokenApiResponse response)
{
int responseCode = response.GetResponseCode();
string accessToken = response.GetAccessToken();
string refreshToken = response.GetRefreshToken();
return new AuthToken(responseCode, accessToken, refreshToken);
}
public static AuthToken NewSigInToken(TokenApiResponse response)
{
int responseCode = response.GetResponseCode();
string accessToken = response.GetAccessToken();
string refreshToken = response.GetRefreshToken();
int authCode = response.GetAuthorizationCode();
// 토큰 유무 확인
bool IsTokenSigin = response.GetAccessToken() != null ? true : false
// 보통 여기서 null체크를 하고, null이라면 서버 응답값이 잘못된 것이므로 thow 날려주면 validation 체크도 완료!
// 토큰 로그인 인가?
if(IsTokenSigin)
{
return new AuthToken(responseCode, accessToken, refreshToken);
}
else
{
// 토큰 로그인이 아니면, 코드 로그인이겠군!!
return new AuthToken(responseCode, authCode);
}
}
public static AuthToken NewSigOutAuthToken(TokenApiResponse response)
{
int responseCode = response.GetResponseCode();
string accessToken = response.GetAccessToken();
string refreshToken = response.GetRefreshToken();
return new AuthToken(responseCode, accessToken, refreshToken);
}
}
캡슐화 이후 여러분이 해야할 작업량은 아래와 같습니다.
public AuthClient
{
public AuthToken SignIn()
{
// 로그인 시도
TokenApiResponse response = TokenApiApiRequest(SIGNIN);
return AuthToken.NewSigInToken(response);
}
public AuthToken SignOut()
{
// 로그아웃
TokenApiResponse response = TokenApiApiRequest(SIGNOUT);
return AuthToken.NewSigOutAuthToken(response);
}
public AuthToken RefreshToken()
{
// 토큰 갱신
TokenApiResponse response = TokenApiApiRequest(REFRESH);
return AuthToken.NewRefreshAuthToken(response);
}
}