최데브는 오늘도 프로그래밍을 한다.

3. Java Spring 과 안드로이드 통신 , DB로 데이터 전달까지 본문

안드로이드 프로젝트

3. Java Spring 과 안드로이드 통신 , DB로 데이터 전달까지

최데브 2020. 12. 8. 01:42
반응형

스프링에 대한 이야기를 다 적고 안드로이드로 넘어가려고 했으나

그렇게 되면 중복되는 이야기가 많을 것 같아서 안드로이드와 함께 이야기를 하려고한다.

그게 다시 읽게 되어도 이해하기 수월하고 시간의 흐름도 맞을거 같다.

 

나는 서버와 안드로이드가 통신할때 Retrofit 이라는 라이브러리를 사용했다.

 

Restful api 통신을 하기에 아주 적합한 라이브러리고

 

1. 속도가 기존의 Okhttp 에 비해서 속도가 빠르다는 장점이 있다.

2. 구현 또한 HttpUrlConection의 사용보다 훨씬 간단하여 몇줄만으로도 완벽하게 작동하는것을 볼 수 있다.

3. 그리고 개인적으로 생각하기에 가장 큰 장점은 동기/비동기를 구현하기가 매우 편리하다는 점이다.

 

먼저 retrofit 을 사용하려면 retrofit 객체를 생성해줘야한다.

사용할때마다 객체를 생성해줘도 되겠지만 서버랑 통신을 할 일이 많은 경우는

엄청난 리소스 낭비가 될 수도 있다. 

여기저기서 많이 호출할것이라고 판단하여 

싱글톤 패턴으로 Retrofit 객체를 구현했다.

 

제대로 된 싱글톤 패턴인지는 모르겠으나 문제없이 작동한다.

구현 코드는 아래와 같다.

 

public class NetRetrofit {


    private  static NetRetrofit outInstance = new NetRetrofit();
    public static NetRetrofit getInstance(){ // 싱글톤으로 만들어줌.
        return   outInstance;
    }

    public static Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("자신이 만든 서버 ip 주소")
            
            .client( new OkHttpClient.Builder()
                    .hostnameVerifier(new NullHostNameVerifier())
                    .sslSocketFactory(SSLUtil.getPinnedCertSslSocketFactory(context))
            .build()) // 이 부분은 글쓴이가 ssl 인증서를 통해 https 통신을 하기위해 추가한 부분 해당사항이 없다면 생략해도 된다
            
            .addConverterFactory(GsonConverterFactory.create()) // gson 을 사용할경우
            //만약 String으로 처리하고 싶다면addConverterFactory(ScalarsConverterFactory.create()) 을 사용
            .build(); // 스프링 서버에 연결하고 레트로핏 초기화

    public static RetrofitService service = retrofit.create(RetrofitService.class);
   

    public RetrofitService getService(){// 싱글톤으로 만들어줌. 다른데서 이 함수를 호출하면 여기서
        //만든 retfrofit service 를 사용할 수 있게 됌.
        return service;
    }
}

이렇게 구현하면 하나의 객체로 앱이 종료될때까지 여러번 생성되지 않고 자원을 아끼면서 레트로핏 통신을 할 수 있게 된다. 이렇게 만들어진 레트로핏을 어떻게 호출하는지는 뒤에 작성하도록 하겠다.

 

<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-8976187698101694"
     data-ad-slot="7038030265"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>

레트로핏은 api 로 받아올 데이터를 담거나 보내줄때 매핑할 DTO 클래스를 객체로 사용하여데이터를 주고 받는다. DTO 객체는 보통 이런식으로 만든다.

 

public class DTO {


    private String userid;
    private  int whatAlarm;
    private int onoff;
    
     public String getUserid() {
        return userid;
    }
    public void setUserid(String userid) {
        this.userid = userid;
    }
    public int getWhatAlarm() {
        return whatAlarm;
    }
    public void setWhatAlarm(int whatAlarm) {
        this.whatAlarm = whatAlarm;
    }
    public int getOnoff() {
        return onoff;
    }
    public void setOnoff(int onoff) {
        this.onoff = onoff;
    }
}

사용하고자 하는 변수들을 만들고 getter 와 setter 를 만든다.

안드로이드 스튜디오에서는 단축기로 Alt + insert 키를 누르면 만들 수 있다.

그리고 Gson 이라는 라이브러리가 있는데 Map 이나 Class 등 객체들을 쉽게 Json 으로

타입에 맞게 알아서 변환해준다.

 

        MemberDTO dto = new MemberDTO();
        dto.setUserId(id);
        Gson gson = new Gson();
        String objJson = gson.toJson(dto); // DTO 객체를 json 으로 변환

위처럼 이렇게 간단하게 MemberDTO 라는 객체를 Json 으로 변환해준다.

 

객체로 만들고 싶지 않다면 아래와 같이 변환해도 충분히 사용가능하다.

물론 addProperty 앞에 적은 "count" 라는 타입명을 서버에서도 똑같이 호출해줘야한다.

 

        int aaac = 3;
        Gson gson = new Gson();
        JsonObject object = new JsonObject();
        object.addProperty("count", aaac);
        String objJson = gson.toJson(object);

 

그리고 사용하기전에 Interface 로 레트로핏 동작에 대한 설명을 코드로 적어줘야하는데 

위에서 레트로핏 객체를 싱글톤으로 만드는 코드에 있는 RetrofitService.class의 코드는 아래에 있다.

 

public interface RetrofitService {

    //유저 정보 업데이트
    @FormUrlEncoded
    @POST("userInfoUpdate")
    Call<ResponseBody> userInfoUpdate(@Field("objJson") String objJson);
    
    
}

POST , GET , PUT 등등 어떤 방식으로 데이터를 주고 받을지 적고 그 뒤에는 spring 에서 컨트롤러에서 받을

url 을 적는다.  Restful api 의 형식을 지키지는 못했지만 나는  userInfoUpdate 라고 적어주었다.

 

 

그리고 실 사용은 아래 코드처럼 사용 할 수 있다.

 Call<ResponseBody> userInfoUpdate = NetRetrofit.getInstance().getService().userInfoUpdate(objJson);
        userInfoUpdate.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                if(response.isSuccessful()){
                    Log.d("res", "성공");

                }
            }
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.d("fail", t.getMessage());
            }
        });

ResponseBody 자리는 어떤 타입으로 응답을 받을건지 적어두는 곳이다.

만약 응답을 MemberDTO 로 받아오고 싶다면 그 자리에 MemberDTO 를 적어서 구현하면 된다.

그럼 서버쪽에서 받아온 MemberDTO 객체가 서버와 통신을 성공했을때 호출되는

OnResponse 에서 response 객체로 넘어오게 된다. 

 

만약 MemberDTO 에 String userid 라는 멤버변수가 있다고 한다면

MemberDTO dto = response.body();

String userid = dto.getUserid();

이런식으로 사용할 수 있다. 

 

여기까지는 안드로이드 이야기 아래에는 저 retrofit 통신을 받고 데이터를 전달해주는 Spring 서버를 적어보겠다.


 spring 에서 userInfoUpdate 의 url 로 레트로핏 통신을 요청하게 되면 mvc 패턴에서는 

Controller 에서 url 로 응답을 받는다. 아래 코드처럼 동작한다.

 

	// 사람 정보 업데이트
	@ResponseBody
	@RequestMapping(value = "/userInfoUpdate", method = { RequestMethod.GET,
			RequestMethod.POST }, produces = "application/json;charset=utf-8")
	public void UserUpdate(String objJson) throws Exception {

		Gson gson = new Gson();
		Member member = gson.fromJson(objJson, Member.class);

		memberJoinService.userUpdate(member);
	}

@RequestMapping 어노테이션으로 value 값에 어떤 url의 요청을 처리하는지 적어준다.

그럼 레트로핏으로 userInfoUpdate 라는 url 요청이 왔을때 이 컨트롤러에 있는 내부 동작을 실행한다.

위 코드에서는 memberJoinService의 userUpdate 라는 함수를  내부에서 실행한다.

 

UserUpdate 라는 메소드를 보면 String objJson 을 매개변수로 받고 있는데 이 변수는 

레트로핏 통신을 할때 안드로이드에서 넘겨준 MemberDTO 가 JSON 형식으로 바뀌어서 들어가있다.

이 곳에서도 GSON 라이브러리를 이용하여 객체로 자동 매핑을 해주는데 이렇게 사용하면

gson.fromJson 이라는 코드 한줄로 쉽게 DTO 객체를 만들어 줄 수가 있다.

(이때 서버에 있는 dto 와 안드로이드에 있는 dto 객체의 멤버변수명은 동일해야 제대로 작동한다.)

 

다음은 Service 파일을 보도록 하겠다. 이 전의 포스팅을 보면 mvc 패턴을 간략하게 도형으로 그려놓은 이미지가 있는데

그곳에 service가 보일것이다. 그림에는 service만 적혀있지만 나의 경우는  service 와함께 service interface 도 함께 구현해서 개발했다.

 

처음에 spring mvc 에 대해서 공부할때부터 이렇게 배운지라 큰 의문점 없이 개발을 했는데

자꾸 만들다보니 굳이 필요하지도 않는거 같은데 왜 이렇게 개발을 하나 꼭 이 구조를 지켜야하나? 

의문점이 들어서 찾아도보고 개인적으로 고민도 했는데 딱 이거다 하는 시원한 대답은 찾지 못했다.

 

그래서 개인적인 의견으로는 

 

1. MVC 패턴을 위한 구조는 아니다. interface 를 사용해서 구현해도 되지만 필수는 아니다.

2. 웹서버에서는 service와 dao가 같이 컨트롤러 밖에서 제공되기 때문에 혹시나 뒤에 유지보수에서 바뀔수 있는 구조를

   염려해서 만드는것이다.

3. 인터페이스를 사용하는 이유는 개인적으로 정의를 내리자면 프로그램의 구조를 명확하게 하고 규약을 정해서 통일된 코드를 유지하기 위해서라고 생각하고 있는데 이 또한 비슷한 이유라고 생각한다. 

4. 재사용성을 높인다고 생각할수도 있지만 내가 실력이 부족해서인지 설계를 이상하게 해서인지는 몰라도 새로운 기능을 구현할때마다 하나씩 새로 만들어줬었다. 재사용은 긴가민가하다. 모든 기능에 공통적으로 포함되는 기능이 있다면 가능할수도 있겠다.

 

위는 어디까지나 개인적인 의견이며 틀린 말이 많이 있을 수 있으므로 '이게 정답이구나' 라고 생각하시는분은 없길바라며...

 

아래는 위에서 장황하게 설명했던 그 serviceInterface 다. 여기서는 구현체에 들어갈 함수들 껍데기만 만들어준다.

이렇게 해두면 혹시나 기능이나 전달받는 매개변수 또는 반환값을 빠뜨리거나 다르게 만들지 않고

규약을 지키면서 실수없이 프로그래밍을 할 수 있다. 

public interface MemberJoinService {
	// User 정보 업데이트(수정)
	public void userUpdate(Member member) throws Exception;
}

아래 코드는 인터페이스를 implements 한 구현체다.

실제 동작은 이 구현체에서 적혀있지만 컨트롤러에서는 MemberJoinService를 호출하면 정상적으로 동작한다.

@Service
public class MemberJoinServiceimple implements MemberJoinService {
    
    
	@Inject
	private memberDAO dao;
    
    @Override
	public void userUpdate(Member member) throws Exception {
		dao.userUpdate(member);
	}
 }

여기보면 가끔씩 글에 등장했던 dao 가 나오는데 이 dao 도 이전 포스팅의 mvc 그림에 등장한다.

Data Access Object 의 약자로 직접 DB와 통신하는 부분은 따로 분리해서 만든 것이다.

 

장황하게 설명하기 보다는 바로 코드를 보겠다.

 

@Repository
public class memberDAOimple implements memberDAO {

	@Inject
	private SqlSession sql;
	private static String namespace = "com.xxx.mappers.memberMapper";
    
    	// User 정보 업데이트
	@Override
	public void userUpdate(Member member) throws Exception {
		sql.update(namespace + ".userUpdate", member);
	}
   
  }

resouce 폴더 아래에 만들어둔 memberMapper.xml 에 userUpdate 라는 id를 가진 update 쿼리를 실행하게 한다.

이때 쿼리문에 member 라는 객체를 저런식으로 넘겨줄 수 있다.

 

그렇다면 sql 에서는 이 객체를 어떤 식으로 이용할까?

 

	<!-- User 정보 업데이트 -->
	<update id="userUpdate">
		UPDATE
		tbl_member
		SET
		nickname = #{nickname}, usermsg = #{usermsg} ,gender =#{gender},age=#{age}
		where
		userid = #{userId}
	</update>

 #{nickname} 이런식으로 사용할 수 있다 . 이때 들어가는 변수명들은 dto의 멤버변수의 이름과 동일하게 적어주면

해당 값들이 쿼리에서 제대로 작동한다.

 

Mybatis 를 사용한것인데 이것에 대한 환경설정이나 쿼리에서 사용하는 if 문등 여러가지 정보는 인터넷에 검색하면 많이 나오니 참고하면 된다.

 

쓰다보니 글이 길어졌는데 정리를 하자면

 

1. 안드로이드 멤버 정보 업데이트 버튼 클릭

2. 레트로핏 통신으로 dto 객체를 Gson 라이브러리를 이용하여 json 으로 담아 url 로 서버에 요청

3. 서버에서 해당 url 를 담당하는 controller 가 값을 받아서 내부로직 실행

4. service 로 값을 전달

5. dao에서 값을 받고 Mybatis 를 이용하여 쿼리실행

6. Mysql에  값이 변경됐음을 확인가능

 

으로 요약 할 수 있다.

 

세세한 설명을 적기에는 너무 길어져서 적지 못했지만 전체적인 흐름을 파악하는데 도움이 될 것이라고 생각한다.

미래의 내가 혹시나 헷갈린다면 읽어보기를 바란다..

반응형

'안드로이드 프로젝트' 카테고리의 다른 글

4. 안드로이드 MVP 패턴  (0) 2020.12.08
2. Java Spring 서버(1)  (0) 2020.12.08
1. 전체적인 프로젝트 개요  (0) 2020.12.08
Comments