마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

retrofit2를 이용하여 스프링부트 rest api 통신

0 추천

안녕하세요 질문이 있어 글 남깁니다.

 

서버와 데이터를 주고 받는 코드는 이렇게 되어있고,

callSignUp.enqueue(object : Callback<UserResponse> {
    override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) {
        if (response.isSuccessful && response.code() == ResponseCode.SUCCESS_POST) {
            Log.d("JoinFragment: onResponse: Success:: ", "SUCCESS")
            join = response.body()
            Log.d("Join: MSG:: ", "${join?.signUp}")

            finish()
        }
    }

    override fun onFailure(call: Call<UserResponse>, t: Throwable) {
        Log.e("JoinFragment: onResponse: Failure:: ", "실패 : $t")
    }
})

 

데이터 클래스는 이렇게 되어 있고,

data class UserResponse(
    @SerializedName("signUp")
    var signUp: String = ""

)

 

POST는 이렇게 되어 있습니다.

@FormUrlEncoded
@POST("api/user/join")
fun signUp(
    @Field("name") name: String,
    @Field("phone") phone: String,
    @Field("password") password: String,
    @Field("auth") auth: String
): Call<UserResponse>

 

스프링부트에서 이런식으로 api가 구현되어 있고

@PostMapping("/api/user/join")
public String signUp(UserJoinRequestDto userJoinRequestDto) {
    return "SUCCESS JOIN PHONE : " + userService.save(userJoinRequestDto);
}

 

코틀린에서 서버로 데이터 전송은 잘 되는 상황입니다..

하지만, 현재 return 값을 받고싶은데

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

오류가 뜨며, object이 필요하지만, string이 온다는 내용의 오류가 뜨는 것 같습니다.

retrofit2 사용예제를 보면 이런식으로 잘 구현되는 것 같은데, 어디가 문제인지를 모르겠어요

string으로 보내고 string으로 받겠다는데 왜 안되는 것일까요?

현재 개발 공부중이라 질문이 좀 엉성한 면이 있네요.. 잘못된 단어나 질문에 대해선 피드백 해주셔도 좋은 공부가 될 것 같습니다.

답변해주시면 감사하겠습니다!

 

 

hand (1,150 포인트) 님이 2021년 9월 7일 질문
hand님이 2021년 9월 7일 수정
혹시  Json을 파싱할 수 있는  apdater 를 retrofit에 설정하셨나요? 코드를 보니 Gson을 사용하시는 것 같은데, Retrofit에 gson을 사용할 수 있도록 세팅이 되어있는지 확인해 보세요.
Retrofit.Builder()
            ...
            .addConverterFactory(GsonConverterFactory.create(Gson()))
            ...
            .build()
object RetrofitClient {
    private var instance: Retrofit? = null
    private val gson = GsonBuilder().setLenient().create()

    private const val BASE_URL = "..."

    fun getInstance(): Retrofit {
        if(instance == null) {
            instance = Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build()
        }

        return instance!!
    }
}

현재 이렇게 세팅되어있습니다.

Base_url은 가린 것입니다.
Spring Boot에서는 String type으로 리턴하는게 맞나요?
Postman 같은 툴을 이용해서 먼저 서버쪽 테스트를 해보세요. 서버가 어떤 응답값을 리턴하는지 먼저 체크하셔야 할 듯한데요.
@PostMapping("/api/user/join")
public String signUp(UserJoinRequestDto userJoinRequestDto) {
    return "SUCCESS JOIN PHONE : " + userService.save(userJoinRequestDto);
}

이런식으로 구현했다고 백엔드쪽 담당하는 친구에게 얘기들었어요.

String 타입으로 리턴 되는 것 처럼 보이긴 하는것 같아요
서버에서 정상적인 응답을 한 것이 아니라 에러관련 내용을 리턴할 수도 있고 경우에 따라서는  Html을 리턴하는 경우도 있습니다. 먼저 postman 등을 이용해서 님이 전송하는 요청이 어떤 응답값을 리턴하는지 체크해 보세요. 그리고 안드로이드쪽에서 어떤 요청값이 날아가고 들어오는지 체크하시려면 okhttp3 와  httpLoggingInterceptor를 세팅하세요. 이런 값들이 확인이 되어야 정확한 디버깅이 가능할 겁니다.

Retrofit.Builder()
            ....
            ...
            .client(okHttpClient)
            .build()


    private val httpLoggingInterceptor: HttpLoggingInterceptor by lazy {
        HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
    }

   fun provideOkHttpClient(): OkHttpClient {
        return with(OkHttpClient.Builder()) {
            ...
            addInterceptor(httpLoggingInterceptor)
            ...
..
        }.build()
    }
답변감사합니다! 일단 postman 먼저해보도록 할게요 좋은 정보 감사합니다!!
안녕하세요 혹시 spark 님이 말씀해주신 httpLogginginterceptor를 세팅해보았는데 시간 괜찮으시면 봐주실 수 있으실까요?
해당 코드를 올려보세요.
I/okhttp.OkHttpClient: --> POST [http://ec2-3-34-172-246.ap-northeast-2.compute.amazonaws.com:8080/api/user/join](http://ec2-3-34-172-246.ap-northeast-2.compute.amazonaws.com:8080/api/user/join)
Content-Type: application/x-www-form-urlencoded
Content-Length: 59
I/okhttp.OkHttpClient: name=123&phone=123&password=123&auth=ROLE_ADMIN%2CROLE_USER
--> END POST (59-byte body)
I/okhttp.OkHttpClient: <-- 200 [http://ec2-3-34-172-246.ap-northeast-2.compute.amazonaws.com:8080/login](http://ec2-3-34-172-246.ap-northeast-2.compute.amazonaws.com:8080/login) (233ms)
Set-Cookie: JSESSIONID=342CD6CDCA5B663C77CA037A243F91B3; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: text/html;charset=UTF-8
Content-Length: 1415
Date: Thu, 09 Sep 2021 12:39:29 GMT
I/okhttp.OkHttpClient: Keep-Alive: timeout=60
Connection: keep-alive
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Please sign in</title>
<link href="[https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css](https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css)" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="[https://getbootstrap.com/docs/4.0/examples/signin/signin.css](https://getbootstrap.com/docs/4.0/examples/signin/signin.css)" rel="stylesheet" crossorigin="anonymous"/>
I/okhttp.OkHttpClient:   </head>
I/okhttp.OkHttpClient:   <body>
<div class="container">
<form class="form-signin" method="post" action="/api/user/login">
<h2 class="form-signin-heading">Please sign in</h2>
<p>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
</p>
<p>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
</p>
<input name="_csrf" type="hidden" value="dd3bfc9c-d645-42e2-a929-a28e83ba8169" />
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div>
</body></html>
<-- END HTTP (1415-byte body)
E/JoinFragment: Failure::: 실패 : com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

okHttp3 의 기록은 이런식으로 뜨고 있습니다.
postman에서 확인한 return값은

{
"successString": "SUCCESS JOIN PHONE : 01012341234"
}

이런식으로 되어 있는데, 혹시나 object type으로 보내면 되지 않을까 싶어서 바꾼 것입니다.

스트링 타입으로 반환될때는

SUCCESS JOIN PHONE : 01012341234

이렇게만 나오는 것을 확인할 수 있었습니다.

혹시나 제가 부족해서 못알려드린 코드는 말씀해주시면 댓글 달겠습니다!
헤더가 Content-Type: text/html;charset=UTF-8 로 되어 있네요. 이러면 서버에서 JSON 포맷을 리턴하지 못할겁니다. 그래서 응답내용이 html로 온 것 같구요. postman의 경우는 요청에 필요한 헤더값들을 자동으로 세팅해 주는 듯합니다. 어쨋든 서버에서 Content-Type에 application/json을 리턴해야 클라이언트가 응답값을 수신할 때 JSON 포맷으로 인식할 겁니다. 이번에 웹브라우져에서 API의 URL 을 직접 열어서 postman과 동일한 포맷으로 수신이 가능한지 체크해 보세요. 안될 것 같은 생각이 드네요. 그렇다면, SpringBoot로 작성한 서버쪽 코드가 잘못된 것입니다.
한가지 떠오르는 건, 아마도 SpringBoot가 시큐러티 모듈이 들어가 있어서 로그인이 안되었을 경우에는 로그인 페이지를 리턴하는 것 같은데, 이 부분도 체크해 보세요.
늦은시간에 답변 주셔서 정말 감사합니다. 시도해보도록 하겠습니다!!
안녕하세요 spark님 spark님이 말씀하신게 맞았네요 시큐러티 모듈이 들어가서 로그인이 안되었을 경우 로그인 페이지를 리턴했던 것이었습니다. 주석처리해주니 바로 원하는 데이터를 리턴받을 수 있었습니다! 도움 주셔서 정말 감사합니다!!

답변 달기

· 글에 소스 코드 보기 좋게 넣는 법
· 질문에 대해 추가적인 질문이나 의견이 있으면 답변이 아니라 댓글로 달아주시기 바랍니다.
표시할 이름 (옵션):
개인정보: 당신의 이메일은 이 알림을 보내는데만 사용됩니다.
스팸 차단 검사:
스팸 검사를 다시 받지 않으려면 로그인하거나 혹은 가입 하세요.
...