카카오 로그아웃 401 에러

vsc에서 리액트 네이티브, 액시오스를 기반으로 모바일 애플리케이션 제작 중입니다. webview 라이브러리를 통해 카카오 로그인을 구현 중에 있고요… 현재는 카카오 로그인은 성공하여서 카카오 계정으로 외부 앱인 저희 앱에 등록이 되어있습니다.

아래는 로그인에서 쓴 함수입니다.

function LogInProgress(data) {
        // access code는 url에 붙어 장황하게 날아온다.
        // substringd으로 url에서 code=뒤를 substring하면 된다.

        console.log("data :: " + data);

        const exp = "code=";
        var condition = data.indexOf(exp)

        if (condition != -1) {
            var request_code = data.substring(condition + exp.length);

            // console.log("access code :: " + request_code);
            // 토큰값 받기
            requestToken(request_code);
            setLoggedIn(true);
        }
    };

    const requestToken = async (request_code) => {
      var returnValue = "none";
      var request_token_url = "https://kauth.kakao.com/oauth/token";

      console.log("request_code :: " + request_code);

      axios({
          method: "post",
          url: request_token_url,
          params: {
              grant_type: 'authorization_code',
              client_id: '저의 앱 키값',
              redirect_uri: 'https://auth.expo.io/',
              code: request_code,
          },
      }).then(function (response) {
        axios({
          method:'get',
          url:'https://kapi.kakao.com/v2/user/me',
          headers:{
              Authorization: `Bearer ${response.data.access_token}`
          }
      }).then(function (response) {
          console.log('response22 :: ' + JSON.stringify(response));
          setResult(response.data)
      }).catch(function (error) {
        console.log('error', error);
      });
      }).catch(function (error) {
          console.log('error', error);
      });
  };

setResult(response.data)에 사용자 값을 받았습니다…
문제는 로그아웃인데 아래는 그 코드입니다.

const logout = async (access_token) => {
    try {
      const response = await axios({
        method: 'post',
        url: 'https://kapi.kakao.com/v1/user/logout',
        headers: {
          Authorization: `Bearer ${access_token}`,
        },
      });
  
      if (response.status === 200) {
        console.log('로그아웃 성공');
        setLoggedIn(false);
        setResult(null);
      } else {
        console.log('로그아웃 실패');
      }
    } catch (error) {
      console.error(error);
    }
  }

네… 이렇게 작성을 했는데 401에러가 뜨고 있습니다.
토큰 문제일 수 있다는 것을 보고 result값을 초기화한 후 다시 로그인을 통해 사용자 정보를 다시 받은 후 콘솔 출력해봤는데 잘 받아오더라고요… 토큰 만료가 문제는 아닌 것 같은데 어디가 문제인지 인지가 힘든데… 한번 봐주시면 감사하겠습니다…

안녕하세요.

확인을 위해 앱 ID 부탁드립니다.


앱ID
https://developers.kakao.com/ 의 내 애플리케이션>앱 설정>요약 정보 : 기본정보에 있는 앱 ID
숫자로된 ID 입니다
ex) 123456

888748 입니다…

해당 디벨로퍼스앱에 /v1/user/logout 호출이력이 없는 것으로 볼때

헤더의 액세스 토큰이 잘못 설정된듯합니다.

에러 발생할때 사용하신 액세스토큰 기재해주시면 추가로 로그 확인해보겠습니다.

로그아웃 함수에서 쓴 토큰을 콘솔 출력해보면

토큰 :  Class {
  "_dispatchInstances": FiberNode {
    "tag": 5,
    "key": null,
    "type": "RCTView",
  },
  "_dispatchListeners": [Function onResponderRelease],
  "_targetInst": FiberNode {
    "tag": 5,
    "key": null,
    "type": "RCTView",
  },
  "bubbles": undefined,
  "cancelable": undefined,
  "currentTarget": ReactNativeFiberHostComponent {
    "_children": Array [
      ReactNativeFiberHostComponent {
        "_children": Array [
          13,
        ],
        "_internalFiberInstanceHandleDEV": FiberNode {
          "tag": 5,
          "key": null,
          "type": "RCTText",
        },
        "_nativeTag": 15,
        "viewConfig": Object {
          "Commands": Object {},
          "bubblingEventTypes": Object {
            "topChange": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onChange",
                "captured": "onChangeCapture",
              },
            },
            "topPointerCancel": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onPointerCancel",
                "captured": "onPointerCancelCapture",
              },
            },
            "topPointerDown": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onPointerDown",
                "captured": "onPointerDownCapture",
              },
            },
            "topPointerEnter": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onPointerEnter",
                "captured": "onPointerEnterCapture",
                "skipBubbling": true,
              },
            },
            "topPointerLeave": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onPointerLeave",
                "captured": "onPointerLeaveCapture",
                "skipBubbling": true,
              },
            },
            "topPointerMove": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onPointerMove",
                "captured": "onPointerMoveCapture",
              },
            },
            "topPointerOut": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onPointerOut",
                "captured": "onPointerOutCapture",
              },
            },
            "topPointerOver": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onPointerOver",
                "captured": "onPointerOverCapture",
              },
            },
            "topPointerUp": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onPointerUp",
                "captured": "onPointerUpCapture",
              },
            },
            "topSelect": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onSelect",
                "captured": "onSelectCapture",
              },
            },
            "topTouchCancel": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onTouchCancel",
                "captured": "onTouchCancelCapture",
              },
            },
            "topTouchEnd": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onTouchEnd",
                "captured": "onTouchEndCapture",
              },
            },
            "topTouchMove": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onTouchMove",
                "captured": "onTouchMoveCapture",
              },
            },
            "topTouchStart": Object {
              "phasedRegistrationNames": Object {
                "bubbled": "onTouchStart",
                "captured": "onTouchStartCapture",
              },
            },
          },
          "directEventTypes": Object {
            "onGestureHandlerEvent": Object {
              "registrationName": "onGestureHandlerEvent",
            },
            "onGestureHandlerStateChange": Object {
              "registrationName": "onGestureHandlerStateChange",
            },
            "topAccessibilityAction": Object {
              "registrationName": "onAccessibilityAction",
            },
            "topClick": Object {
              "registrationName": "onClick",
            },
            "topContentSizeChange": Object {
              "registrationName": "onContentSizeChange",
            },
            "topInlineViewLayout": Object {
              "registrationName": "onInlineViewLayout",
            },
            "topLayout": Object {
              "registrationName": "onLayout",
            },
            "topLoadingError": Object {
              "registrationName": "onLoadingError",
            },
            "topLoadingFinish": Object {
              "registrationName": "onLoadingFinish",
            },
            "topLoadingStart": Object {
              "registrationName": "onLoadingStart",
            },
            "topMessage": Object {
              "registrationName": "onMessage",
            },
            "topMomentumScrollBegin": Object {
              "registrationName": "onMomentumScrollBegin",
            },
            "topMomentumScrollEnd": Object {
              "registrationName": "onMomentumScrollEnd",
            },
            "topScroll": Object {
              "registrationName": "onScroll",
            },
            "topScrollBeginDrag": Object {
              "registrationName": "onScrollBeginDrag",
            },
            "topScrollEndDrag": Object {
              "registrationName": "onScrollEndDrag",
            },
            "topSelectionChange": Object {
              "registrationName": "onSelectionChange",
            },
            "topTextLayout": Object {
              "registrationName": "onTextLayout",
            },
          },
          "uiViewClassName": "RCTText",
          "validAttributes": Object {
            "accessibilityActions": true,
            "accessibilityCollection": true,
            "accessibilityCollectionItem": true,
            "accessibilityHint": true,
            "accessibilityLabel": true,
            "accessibilityLabelledBy": true,
            "accessibilityLiveRegion": true,
            "accessibilityRole": true,
            "accessibilityState": true,
            "accessibilityValue": true,
            "adjustsFontSizeToFit": true,
            "alignContent": true,
            "alignItems": true,
            "alignSelf": true,
            "allowFontScaling": true,
            "android_hyphenationFrequency": true,
            "aspectRatio": true,
            "backgroundColor": Object {
              "process": [Function processColor],
            },
            "borderBottomWidth": true,
            "borderEndWidth": true,
            "borderLeftWidth": true,
            "borderRightWidth": true,
            "borderStartWidth": true,
            "borderTopWidth": true,
            "borderWidth": true,
            "bottom": true,
            "collapsable": true,
            "columnGap": true,
            "dataDetectorType": true,
            "disabled": true,
            "display": true,
            "dynamicTypeRamp": true,
            "elevation": true,
            "ellipsizeMode": true,
            "end": true,
            "flex": true,
            "flexBasis": true,
            "flexDirection": true,
            "flexGrow": true,
            "flexShrink": true,
            "flexWrap": true,
            "gap": true,
            "height": true,
            "importantForAccessibility": true,
            "isHighlighted": true,
            "isPressable": true,
            "justifyContent": true,
            "left": true,
            "lineBreakStrategyIOS": true,
            "margin": true,
            "marginBottom": true,
            "marginEnd": true,
            "marginHorizontal": true,
            "marginLeft": true,
            "marginRight": true,
            "marginStart": true,
            "marginTop": true,
            "marginVertical": true,
            "maxFontSizeMultiplier": true,
            "maxHeight": true,
            "maxWidth": true,
            "minHeight": true,
            "minWidth": true,
            "minimumFontScale": true,
            "nativeID": true,
            "numberOfLines": true,
            "onInlineViewLayout": true,
            "onLayout": true,
            "onMoveShouldSetResponder": true,
            "onMoveShouldSetResponderCapture": true,
            "onPointerEnter": true,
            "onPointerEnterCapture": true,
            "onPointerLeave": true,
            "onPointerLeaveCapture": true,
            "onPointerMove": true,
            "onPointerMoveCapture": true,
            "onPointerOut": true,
            "onPointerOutCapture": true,
            "onPointerOver": true,
            "onPointerOverCapture": true,
            "onResponderEnd": true,
            "onResponderGrant": true,
            "onResponderMove": true,
            "onResponderReject": true,
            "onResponderRelease": true,
            "onResponderStart": true,
            "onResponderTerminate": true,
            "onResponderTerminationRequest": true,
            "onShouldBlockNativeResponder": true,
            "onStartShouldSetResponder": true,
            "onStartShouldSetResponderCapture": true,
            "onTextLayout": true,
            "onTouchCancel": true,
            "onTouchEnd": true,
            "onTouchMove": true,
            "onTouchStart": true,
            "opacity": true,
            "overflow": true,
            "padding": true,
            "paddingBottom": true,
            "paddingEnd": true,
            "paddingHorizontal": true,
            "paddingLeft": true,
            "paddingRight": true,
            "paddingStart": true,
            "paddingTop": true,
            "paddingVertical": true,
            "position": true,
            "renderToHardwareTextureAndroid": true,
            "right": true,
            "rotat...(truncated to the first 10000 characters)

이렇게 출력되는데 이 토큰이 아닌가요?..

const logout = async (access_token) => {
    try {
      const response = await axios({
        method: 'post',
        url: 'https://kapi.kakao.com/v1/user/logout',
        headers: {
          Authorization: `Bearer ${access_token}`,
        },
      });
  

위 코드 ${access_token}` 변수에 담긴 값 알려주시겠어요?
기재하신 부분은 액세스 토큰과 무관한 내용 같네요.

const logout = async (access_token) => {
    console.log("토큰 : ", access_token);
    try {
      const response = await axios({
        method: 'post',
        url: 'https://kapi.kakao.com/v1/user/logout',
        headers: {
          Authorization: `Bearer ${access_token}`,
        },
      });
  
      if (response.status === 200) {
        console.log('로그아웃 성공');
        setLoggedIn(false);
        setResult(null);
      } else {
        console.log('로그아웃 실패');
      }
    } catch (error) {
      console.error(error);
    }
  }

… 콘솔 출력한 값이 위의 값처럼 나옵니당…

네, ${access_token} 변수에 잘못된 값이 할당되어 401에러가 발생했군요.

카카오 로그인 후, 리다이렉트 URI에서 인가코드로 토큰발급 받으시고 아래 가이드와 같이 응답의 access_token값을 변수에 할당하셔서 호출해주세요.

REST API | Kakao Developers REST API

로그아웃 함수를 수정했는데 혹시 앱ID가 888748인 디벨로퍼스앱에 /v1/user/logout 호출이력이 있는지 확인할 수 있을까요?.. 감사합니다…

최근 해당 디벨로퍼스앱에 로그아웃 이력은 없네요.