본문 바로가기

카테고리 없음

Spring 미니 프로젝트 [RN & Spring] - Swagger & 연동

이번에는 제작한 API를 한 눈에 파악하는 Swagger를 사용해보고 RN Expo와 연동하려고 한다.

 

Swagger

개발했던 API가 잘 돌아가는지 확인이 가능하고, API 설명서 사이트가 완성된다.

 

build.gradle에 의존성을 추가한다.

dependencies {
    // ... 기존 의존성들 ...
    
    // ⭐️ Swagger (SpringDoc) 추가
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
}

 

gradle 새로고침 필수!

http://localhost:8080/swagger-ui/index.html
서버를 재시작하면 인터넷 브라우저에서 직접 확인해볼 수 있다.

 

 

등록하고 조회하는 활동이 정상적으로 이루어짐을 확인할 수 있다.

 

RN Expo 연동

다른 부가적인 로직들을 더 개발하기 전에 정말로 연동이 되어서 걸음 수가 DB에 저장이 되는지 확인하고 넘어가기로 결정했다!

 

우선 프론트 코드에서 만보기 화면이 백그라운드로 가거나 비활성화될 때 서버에 요청을 보내서 걸음 수를 저장하도록 했다

const saveStepsToServer = async (currentSteps) => {
    try {
      const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD 형식

      const API_BASE_URL = "http://192.168.35.83:8080";

      const response = await fetch(`${API_BASE_URL}/api/steps`, {
        method: 'POST',
        headers:{
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          userId: 'user1',
          stepCount:currentSteps,
          recordDate :today
        }),
      });

      if(response.ok){
        console.log(`[성공] ${currentSteps}보 서버 저장 완료`);
      }else{
        const errorData = await response.json();
        console.log(`[실패] 상태 코드 : ${response.status}, 전체 에러:`, JSON.stringify(errorData));
      }
    } catch (error) {
      console.error(`[에러] 서버 저장 중 오류 발생: ${error}`);
    }
  };

 

걸음 수를 서버에 저장해달라는 POST 메서드를 사용해서 보낸다. 
이게 HTTP 강의에서 듣던 클라이언트 코드이다! 직접 메서드와 헤더까지 설정해줄 수 있다.

 

메시지 바디로는 postman처럼 했던 것처럼 json 형태로 보내본다.

여기서는 stringfy 라는 것을 써서 형식을 맞춰서 보낼 수 있다.

 

스마트폰과 웹 서버 통신을 위해서 여러 종류의 통신 후보들이 있는데

RN Expo로 확인할 예정이라 같은 와이파이를 공유해야 한다.

 

컴퓨터의 내부 주소와 무조건 똑같은 주소를 사용해야 한다

useEffect(() => {
    const subscription = AppState.addEventListener('change', (nextAppState) => {
      // 앱이 'active' 상태(화면으로 돌아옴)가 되었고, 플레이어가 준비된 상태라면
      if (nextAppState === 'active') {
        player.play(); // 다시 재생!
      }
      else if(nextAppState.match(/inactive|background/)){
        console.log("앱이 백그라운드로 이동, 현재 걸음 수 서버에 저장 시도...");
        saveStepsToServer(latestSteps.current); // 최신 걸음 수 서버에 저장
      }
    });

 

useEffect 로 변하는 값을 처리할 수 있다. 

 

트러블 슈팅

TypeError: Network request failed 이런 타입 에러가 떴다

스마트폰에서 컴퓨터로 가는 길을 찾지 못한 것이다.

제미나이가 몇 가지 확인 방법을 알려주었다.

  • 스마트폰에서 스웨거 화면 띄워보기
    • 잘 작동했다 -> 네트워크 연결은 정상이다
    • 스웨거는 서버가 작동될 때 실행된다.
  • IP 주소 재확인
  • 같은 와이파이망 확인
  • 윈도우 방화벽

다 이상 없었다. 그렇다면 스프링 부트가 데이터를 받지 않거나 RN이 데이터를 주지 않는 상황?

 

  • 스프링 부트 CORS 정책
    • 스프링 부트는 기본적으로 직접 만든 웹페이지(스웨거)가 아닌, 외부 앱이나 엉뚱한 주소에서 들어오는 요청은 보이스피싱처럼 취급해서 매몰차게 차단해버린다고 한다.
@CrossOrigin(origins = "*") 
@RestController
@RequestMapping("/api/steps")
@RequiredArgsConstructor
public class StepController {
    // ... 기존 코드 동일 ...
}

그래서 이런 코드를 넣어주었다. 외부에서 요청하든 어디서 요청하든 다 받아준다는 의미이다.

서버 오류는 해결했는데 저장이 되지 않는 문제가 발생했다.

404 오류를 배웠었는데, 리소스를 찾을 수 없다는 뜻이다. 즉, /api/steps 라는 곳을 못 찾는다.

또한, 에러 코드를 작성해서 메시지가 나오도록 했었는데 스프링 기본 404 에러가 떴다!

 

 

그래서 전체 오류를 확인했다.

네트워트 오류는 아니다. 왜냐하면 서버 저장 중 오류 발생이 뜨지 않는다.

네트워크 통신은 양호했다

 

DB에 user1 도 넣어둔 상황이였다.

 

알고보니 /api/steps 로 요청이 아니라 /steps 로 요청했다,,,

CORS 정책을 위반한 것도 아니였다..

얼른 바꿔주니 걸음 수 저장에 성공했다.

 

 

 

그치만 작성했던 걸음 수 업데이트 로직이 반영되지 않았다.

같은 날짜의 걸음 수 저장이면 걸음 수가 업데이트 되도록 구현했었다.

 

최신 걸음 수가 프론트 상에서만 구현되고 백엔드로 안 넘어왔다.

const steps = usePedometerStore((s) => s.steps);
  const latestSteps = useRef(steps); // 최신 걸음 수를 저장하는 ref

  const GOAL = 100; //임시 목표 걸음 수 입니다.

  const fillpercent = parseInt((steps / GOAL) * 100);

  useEffect(() => {
        startPedometer();
      }, []);

  useEffect(() => {
    latestSteps.current = steps; // 걸음 수가 업데이트 될 때마다 최신 값 저장
  }, [steps]);

 

이번에는 useRef를 사용해서 최신 걸음 수를 넘겨주는 바구니를 만들어서 아까 만들었던

saveStepsToServer 함수로 넘겨주면 된다! 마치 DTO와 비슷한 느낌이다.

console.log("앱이 백그라운드로 이동, 현재 걸음 수 서버에 저장 시도...");
saveStepsToServer(latestSteps.current); // 최신 걸음 수 서버에 저장

 

 

첫 번째 저장

 

 

두 번째 저장은 걸음 수가 업데이트되어 잘 저장된다!

드디어 연동을 진행하여 데이터가 저장되는 것을 확인했다.

 


 

이제 눈으로 확인했으니 내가 구현하고자 하는 걸음 수 -> 포인트 -> 쿠폰 로직을 제작해보고

조회해서 화면상에 띄워보는 것이 최종 목표이다.

 

그전에 로직을 구현하고 테스트하는 단계를 거쳐서 연동을 끝마치고

DB 를 h2 에서 MySql로 변경해볼 예정이다!