발생 원인

  1. 자식 Component에서 부모 Component의 값을 변경하는 경우
  2. ngAfterViewInit을 이용하여 값을 변경한 경우

에러 발생 과정

  1. Develop 모드에서 변화가 감지되었을 때, Change Detaction이 발생하고 부모 Component부터 체크한다.
  2. OldValue[0]에 값이 저장된다.
  3. OldValue에 저장했던 값과 현재 값을 비교하여 값이 다르면 ExpressionChangedAfterItHasBeenCheckedError를 발생한다.

에러가 발생하는 과정은 위의 3가지가 핵심이다.

 

Angular의 완결함으로 인해 lifecycle이 종료되지 않은 상황에서 lifecycle 시작 단계의 값과 중간 단계의 값이 다르기 때문에 에러가 발생한다.

해결 방법

  • 비동기 처리 핵(hack) : JVM 콜 스택 마지막에 값을 변경하는 방법으로 setTimeout에 시간을 지정하지 않으면 JVM의 콜 스택 마지막에 등록이 된다.
    ...
    
    public isOk: boolean = false;
    
    ngAfterViewInit() {
    	setTimeout(() => {
        	this.isOk = !this.isOk;
        }
    }
    
    ...​
  • ChangeDetectorRef 서비스 : 부모 Component의 ngAfterViewInit 단계에서 강제로 Change Detection를 발생시켜 check 과정을 취소하고 자식 Component에서 변경된 값으로 Check 한다.
    ...
    
    constructor(private changeDetector: ChangeDetectorRef) {}
    
    ngAfeterViewInit() {
    	this.changeDetector.detectChanges();
    }
    
    ...​

 

참고 자료

 

Slacking studio x BLOG

Slacking studio blog.

blog.eunsatio.io

 

ExpressionChangedAfterItHasBeenCheckedError 에러에 대하여

# ExpressionChangedAfterItHasBeenCheckedError 란? - 자식 component에서 부모 component의 값을 바꾸는 경우 - product mode에서는 굳이 에러를 던지진 않지만 developer 모드에서는 에러가 발생 - 값 자체는..

sddev.tistory.com

 

2019-09 Angular를 처음 접하게 되였다.

1년동안 Angular를 사용하면서 느낀 장, 단점을 공유 하고자 한다.
앞으로 사용하면서 느낀 장, 단점도 지속적으로 추가할 예정이다.

 

* 지극히 주관적인 의견입니다. 참고만 해주세요~

 

장점

  • 컴포넌트 단위로 모듈화를 할 수 있어 수정이 용이하다.
  • 화면 구성을 Angular에서 제공하는 기능들을 이용하여 구현하기 쉽다.
    • html에서 for문을 사용, if문으로 조건 걸어 사용, css, style을 데이터에 따라 변경 등을 이용하여 구현한다. 하지만 Angular와 같이 다른 프레임 워크도 충분히 가능하지만 Angular가 기능이 훨씬 많고 편리하다고 생각한다.
    • html 태그에서 사용할수 있는 attribute를 직접 만들어서 사용할 수 있다.
      • <div TestDirective></div> 라고 작성하면 TestDirective attribute에 더블클릭 이벤트를 넣어서 사용할 수 있다.

단점

  • 러닝 커브가 너무 심하다.
    • JavaScript와 jQuery를 접하고 시작했는데도 typescript와 reactive programming, funcational programing등의 파생되는 학습이 필요하여 익숙해지는데까지 시간이 오래 걸린다.
  • 버전 변경이 잦아 업그레이드를 자주 하기에는 리스크가 너무 크다.
    • 6개월에 한번씩 버전이 나온다. 최근 버전은 8.x에서 10.1.4로 업그레이드 하였는데 토이 프로젝트를 업그레이드 후 에러 잡는데 상당한 시간이 걸렸다.

 

많은 프레임 워크를 접한 것은 아니지만 무엇보다 Angular의 기능을 이용하면서 화면 구성하는 게 재미있다.

교차 출처 리소스 공유(Cross-origin resource sharing, CORS), 교차 출처 자원 공유는 웹 페이지 상의 제한된 리소스를 최초 자원이 서비스된 도메인 밖의 다른 도메인으로부터 요청할 수 있게 허용하는 구조이다. 웹페이지는 교차 출처 이미지, 스타일시트, 스크립트, iframe, 동영상을 자유로이 임베드할 수 있다. 특정한 도메인 간(cross-domain) 요청, 특히 Ajax 요청은 동일-출처 보안 정책에 의해 기본적으로 금지된다.

환경

  • Spring Boot (Server)

    • Version : 2.3.0.RELEASE 

    • Port : 8080

  • Angular (Client)

    • Version : 8.2.14

    • Port : 4300


위의 환경 정보에서 Spring Boot, Angular의 Port를 보면 CORS 상황인걸 확인할 수 있다.

Angular에서 카카오 API 인증 요청

  • Kakao Request Guide

GET /oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code HTTP/1.1
Host: kauth.kakao.com

위의 Guide를 보면 client_id, redirect_uri, response_type을 매개변수로 받는것을 알 수 있다.

  • Sample 1

http://kauth.kakao.com//oauth/authorize?client_id=123123123123123123123&redirect_uri=http://localhost:8080/kakao&response_type=code

위의 Sample을 확인해 보면 아래의 3가지 매개변수를 입력한걸 알 수 있다.

client_id : 123123123123123123123

redirect_uri : http://localhost:8080/oauth/kakao

reponse_type : code

 

여기서 redirect_uri가 중요하다.

 

oAuth2의 기본 개념은 Angular(Client)가 Kakao API를 호출하면 Angular(Client)에게 응답을 주는게 맞다.

하지만 Rest API 호출 상황에서는 Kakao API가 Angular(Client)에게 응답을 줄수 있는 방법이 없다.

그래서 redirect_uri를 Spring Boot(Server)로 받아야 한다.

Rest API로 Angular(Client)에서 응답 받는 방법이 없다고 판단하였으나 혹시 모르니 조금 더 찾아보려 한다.
※ Angular(Client)가 Kakao API의 JavaScript 고급가이드대로 Login 하면 가능하다.
하지만 Kakao API 문서에 따르면 앞으로 oAuth 중요 정보인 Refresh Token을 제공하지 않을 계획이라고 명시하였다.
Kakao Developers JavaScript 로그인 API 고급가이드를 참고바란다.

Spring Boot 로직 처리

@GetMapping("/kakao")
public void kakaoLogin(String code, HttpServletRequest request, HttpServletResponse response) throws Exception {
	try {
		RestTemplate rest = new RestTemplate();
          // 사용자 인증정보 받기 위한 세팅
          MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
          map.add("grant_type", "authorization_code");
          map.add("client_id", "12312312312312312312312312");
          map.add("redirect_uri", "http://localhost:8080/oauth/kakao");
          map.add("code", code);

          HttpHeaders headers = new HttpHeaders();
          headers.add("Context-type", "application/json");

          HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);

          // 사용자 인증정보 요청
          ResponseEntity<Map> rs = rest.postForEntity("https://kauth.kakao.com/oauth/token", entity, Map.class);

          // access_token, refresh_token Get
          String Access = rs.getBody().get("access_token").toString();
          String Refresh = rs.getBody().get("refresh_token").toString();

          // 회원 가입 프로세스
          {
              ...
          }

          // Cookie, Response			
          {
              Cookie accessCookie 	= // 쿠키생성
              Cookie refreshCookie 	= // 쿠키생성

              response.addCookie(accessCookie);
              response.addCookie(refreshCookie);

              // Angular로 Redirect
              response.sendRedirect("http://localhost:4300");
          }

	} catch (Exception e) {
		e.printStackTrace();
	}
}

위의 소스를 보면 code를 Kakao API에서 redirect 받는다.

 

받은 인증 코드를 이용하여 사용자 인증정보(access_token, refresh_token)을 요청하고 Cookie를 만들어 response.sendRedirect를 이용하여 Angular(Client)에 redirect하면 정상적으로 Kakao Login이 완료된다.


두서 없이 Kakao Login을 처음 구현하면서 격은 문제점을 보완해서 글을 작성하였습니다. 처음 접하는거라 많이 부족하기도 하고 모르는 것도 많았습니다. 부족한 부분이나 틀린 부분이 있다면 언제든지 지적 해주세요.

 

읽어주셔서 감사합니다.

 

참고 자료

+ Recent posts