이번에는 제작한 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로 변경해볼 예정이다!