[Spring Boot] Flutter로 데이터 가져오기

mi-fasol·2023년 4월 15일
0

Spring

목록 보기
3/4

지난 게시물에서는 Flutter와 Spring boot를 연결하여 데이터를 전송하고 DB에 저장하는 코드를 작성했었다.

이제 Flutter에서 DB에 저장된 게시물 목록을 가져와 보여주고, 게시물 리스트의 아이템을 클릭하면 게시물의 상세 정보를 보여주는 기능을 생성해야 했는데, 어처구니 없는 이유로 오류가 발생한 걸 꼬박 하루만에 깨달아서 나와 같이 헤매고 있는 분들이 계실까 싶어 게시물로 작성한다.

우선, Flutter의 코드부터 살펴보자.

데이터를 전송하는 코드와 마찬가지로 db_service.dart 페이지에 API 통신을 위한 코드를 작성해줬다.

  Future<List<PostResponse>> getAllPost() async {
    final response = await http.get(Uri.parse("api 통신 uri"));
    if (response.statusCode == 200) {
      final data = json.decode(response.body) as List<dynamic>;
      return data
          .map<PostResponse>((json) => PostResponse.fromJson(json))
          .toList();
    } else {
      throw Exception('Failed to load post list');
    }
  }
 
  Future<PostResponse> getPostById(int id) async {
    final response =
        await http.get(Uri.parse("api 통신 uri"));
    if (response.statusCode == 200) {
      final jsonData = json.decode(response.body) as Map<String, dynamic>;
      return PostResponse.fromJson(jsonData);
    } else if (response.statusCode == 404) {
      throw Exception("Post not found");
    } else {
      throw Exception("Failed to fetch Post by Id");
    }
  }

간단히 함수에 대해서 설명하겠다.

  • getAllPost(): 모든 게시물 데이터를 list 형태로 불러오는 함수
  • getPostById(int id): pId를 사용하여 특정 게시물을 불러오는 함수

getAllPost() 함수는 PostResponse 모델을 리스트로 저장해 모든 게시물을 불러오고, getPostById() 함수는 단일 PostResponse 객체를 불러와 특정 게시물의 정보를 받아온다.

나는 pId를 Auto Increase로 설정했기 때문에 Flutter에서 전송을 할 때는 pId가 없는 Post 모델을, 스프링을 통해 데이터를 받아올 때는 pId까지 포함된 PostResponse 모델을 사용했다.

받아올 때 Post 모델을 사용하면 pId 필드가 없기 때문에 pId를 통해 게시물을 상세조회할 수 없고, PostResponse 모델을 통해 서버에 데이터를 전송하면 pId필드가 null로 들어가서 Auto Increase가 아니라 직접 pId를 입력해야 했기 때문에 위와 같은 방법을 선택했다.

Flutter에서는 더 설명할 부분이 없으니 바로 Spring Boot로 넘어가자.

우선 getAllPost() 함수부터 설명하겠다.

PostContoller.java

@GetMapping(produces = "application/json")
@ResponseBody
public List<PostDto> getAllPost(){
    return postService.allCashBoardEntity();
}
    
  • @GetMapping: Get 방식을 통해서 매핑해주는 어노테이션

이 정도면 간단히 설명이 끝난다.
return에 있는 postService.allCashBoardEntity() 함수는 아래와 같다.

public List<PostDto> allCashBoardEntity(){
        return exec();
    }

public List<PostDto> exec(){
    List<Post> postList = new ArrayList<>(postRepository.findAll());

    List<PostDto> postDtoList = new ArrayList<>();

    for(Post post: postList){
        PostDto requestDetailAll = new PostDto();
        requestDetailAll.setPId(post.getPId());
        requestDetailAll.setTitle(post.getTitle());
        requestDetailAll.setContent(post.getContent());
        requestDetailAll.setCategory(post.getCategory());
        requestDetailAll.setPerson(post.getPerson());
        requestDetailAll.setNickname(post.getNickname());
        requestDetailAll.setCreatedAt(post.getCreatedAt());
        requestDetailAll.setType(post.getType());
        postDtoList.add(requestDetailAll);
    }
    return postDtoList;
}

유지보수를 위해 함수를 분리해줬고, exec() 함수에서는 혹시나 pId가 누락되는 상황을 막기 위해 모든 필드에 set을 직접 해줬다.

왜 이렇게 하지?라는 의문이 생기는 것도 당연하나, 나는 코드를 다 작성하고 플러터에서 앱을 실행했을 때 자꾸 pId가 null로 받아와지는 오류가 발생했다.

게시물 리스트는 보이지도 않고 자꾸 아래 사진처럼 "type 'NULL' is not a subtype of type 'int'"라는 오류가 발생하는 거다.

스프링 코드에도 pId가 있고, 데이터베이스에도 있고. 혹시 PostDto에 pId가 안 받아와지나 싶어서 하나하나 set을 해줘도 계속 같은 오류가 발생했었다.

웹에서 api를 사용하면 제대로 Json 데이터가 나오고, pId 필드도 보이는데 이상하게 Flutter에서 게시물 조회가 안 됐다.

그러다가 찾은 것이 아래와 같다.

글씨가 작아서 잘 안 보이겠지만, 자세히 보면 pId 필드가 "pid"로 모두 소문자다. Auto Increase로 자동 생성되는 필드기 때문에 DB에 들어갈 때 그냥 pid로 들어간 거다. 이름이 pid인데, Flutter에서는 자꾸 pId인 속성을 찾으라 하니 당연히 못 찾고 pId 필드가 null로 받아와진 거다.

Flutter의 toJson, fromJson 함수의 pId를 pid로 바꿔주니 아래처럼 정상적으로 작동했다.

나처럼 바보같은 실수를 하지 않았다면, Dto를 따로 안 만들고 그냥

@GetMapping(produces = "application/json")
@ResponseBody
public List<Post> getAllPost(){
    return postRepository.findAll();
}

이렇게만 해줘도 정상적으로 작동한다.

이렇게 게시물을 리스트로 받아오게 했으면 이제 게시물을 상세 조회해줄 차례다.

Flutter의 코드는 위에서 함수를 설명하며 작성해뒀으니 스프링만 보겠다.

PostController.java

@GetMapping("/{id}")
@ResponseBody
public ResponseEntity<Post> getPostById(@PathVariable Long id) {
     Optional<Post> postOptional = postRepository.findById(id);
     return postOptional.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
}

모든 게시물을 가져올 때와 마찬가지로 get 방식을 사용하며, 하나의 게시물만 가져오면 되기에 Post를 반환하도록 했다.

스프링의 findById() 함수를 사용하면 데이터 중에 해당 id 값을 가진 데이터 하나만 반환해준다.

스프링에서 이렇게 작성을 마치고 플러터에서 리스트의 아이템을 클릭해주면

이렇게 게시물의 타이틀과 내용을 보여준다. 아직 페이지 디자인이 완료되지 않았기 때문에 우선은 이렇게 간단하게 보여주도록 작성했다.

스프링을 공부한지는 얼마 되지 않았지만, 지금 내가 작성한 코드들은 어디 가서 "스프링 알아요" 할 정도도 못된다는 것을 안다.
앞으로 좀 더 공부해서 CRUD 말고 다른 기능들도 작성해보고 싶다.

다음 게시물은 Spring의 개념에 대해서 조금 더 알아볼 예정이다.

profile
정위블

0개의 댓글