1. 서론
졸업 작품으로 안드로이드 어플리케이션과 Spring Boot API 서버를 만들기로 했다.
구성 방식은 안드로이드에서 데이터를 요청하면 Spring Boot에서 정보를 리턴하는 방식으로 구성했다.
안드로이드에서 요청을 진행하는 방법은 Retrofit2 라는 라이브러리를 사용했다. 안드로이드 초창기에는 HttpURLConnection, Apache HTTP Client을 사용했는데, 요즘에는 OkHttp나 Retrofit2, Volly를 많이 사용한다고 한다. 다른 라이브러리보다 쓰기도 편하고 코드가 많이 함축되었다.
다음은 프로젝트 세팅을 위한 버전이다.
- Spring Boot : 2.7.3
- Java : 17
- Android Studio : 2021.2.1 Patch 2 for Mac
- OS : Mac OS
2. 안드로이드
안드로이드에서 Retrofit2 라이브러리를 사용하려면 우선 Manifest에서 인터넷 권한을 설정해주고 build.gradle(module)에서 import를 해주어야 한다.
- Manifest
<!-- 네트워크 사용을 위한 퍼미션 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- build.gradle(module)
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
그 후 API 인터페이스를 정의해주어야 한다. 이 인터페이스는 크게 1. 전송할 URL, 2. 전송할 방법, 3. 함수로 구성되어 있다.
import com.example.front.domain.SocialUser;
import com.example.front.dto.ChangePwDTO;
import com.example.front.dto.SignInDTO;
import com.example.front.dto.SignUpDTO;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface RetrofitAPI {
// 일반 회원 로그인 API
@POST("user_login")
Call<Boolean> getLoginResponse(@Body SignInDTO signInDTO);
}
POST 방식은 HTTP 구조에서 Body에 데이터가 들어가기 때문에 @Body를 붙여주어 데이터를 표시해주고, GET 방식은 {}안에 데이터의 이름을 적어주고 @Path("데이터 이름")을 붙여주어 전송한다. @Path를 붙여준 데이터가 ()안에 들어가게 된다.
(ex : String phoneNum이 @Path의 ("phoneNum")에 들어간다.)
함수 작성 방식은 다음과 같다.
@전송 방식(URL)
Call<자료형> 함수이름 (데이터)
- <자료형> : 서버로부터 반환받을 자료형을 의미한다.
- 함수 이름 : 사용할 함수 이름을 의미한다.
- 데이터 : 전달하려는 데이터를 적어준다. 이때 전송 방식에 따라 Annotation을 붙여준다.
다음은 전송할 데이터를 담은 RetrofitClient 객체를 만들어준다.
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitClient {
private static RetrofitClient instance = null;
private static RetrofitAPI retrofitAPI;
private RetrofitClient() {
//retrofit 설정
//사용하고 있는 서버 BASE 주소 ex : http://192.168.0.181
String baseUrl = "사용하고 있는 서버 BASE 주소:8080/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
retrofitAPI = retrofit.create(RetrofitAPI.class);
}
public static RetrofitClient getInstance() {
if (instance == null) {
instance = new RetrofitClient();
}
return instance;
}
public static RetrofitAPI getRetrofitInterface() {
return retrofitAPI;
}
}
객체는 싱글톤 방식으로 만들었고, 객체 내에서 API를 반환할 수 있도록 getRetrofitInterface()를 만들었다.
테스트는 로그인으로 진행하려고 한다.
ID와 비밀번호를 입력하여 Spring Boot 서버에 전송하고 DB에 있는 데이터와 일치하면 true를, 일치하지 않거나 데이터가 없으면 false를 리턴한다.
우선 로그인을 진행할 DTO를 만든다.
보안적인 부분으로 생성자를 public으로 열면 안 되지만, 테스트의 편의를 위해 잠시 열어두었다.
public class SignInDTO implements Serializable {
private String id;
private String pw;
private UserEnum userEnum;
public SignInDTO(String id, String pw, UserEnum userEnum) {
this.id = id;
this.pw = pw;
this.userEnum = userEnum;
}
@Override
public String toString() {
return "SignInDTO{" +
"id='" + id + '\'' +
", pw='" + pw + '\'' +
", userEnum=" + userEnum +
'}';
}
}
그 후 아이디와 비밀번호를 EditText에서 받아와서 SignInDTO를 만든 후 서버로 전송하면 된다. 이 코드에서는 로그인에 성공하면 MainPage로 화면을 넘기고 false를 받으면 다이얼로그 메시지를 띄운다.
public void login(String id, String pw) {
SignInDTO signInDTO = new SignInDTO(id, pw, UserEnum.LoginUser);
//retrofit 생성
retrofitClient = RetrofitClient.getInstance();
retrofitAPI = RetrofitClient.getRetrofitInterface();
//저장된 데이터와 함께 api에서 정의한 getLoginResponse 함수를 실행한 후 응답을 받음
retrofitAPI.getLoginResponse(signInDTO).enqueue(new Callback<Boolean>() {
@Override
public void onResponse(Call<Boolean> call, Response<Boolean> response) {
if (response.isSuccessful() && response.body() != null) {
if (response.body()) {
finish();
Intent intent = new Intent(SignIn.this, MainPage.class);
startActivity(intent);
}
else {
alertDialog("아이디 혹은 비밀번호가 일치하지 않습니다." + "\n" + "다시 시도해주세요.");
}
}
}
@Override
public void onFailure(Call<Boolean> call, Throwable t) {
alertDialog("로그인에 실패하였습니다.");
}
});
}
3. Spring Boot
이제 API 서버인 스프링 서버를 보자.
서버에서는 데이터를 받으면 되기 때문에 MVC Controller를 만들어서 데이터를 받으면 된다.
- User Entity
@Entity
@Getter
@Setter
public class User {
@Id
private String id;
@Column
private String pw;
@Column(nullable = false)
private String phoneNum;
@Column(nullable = false)
private String nickname;
@Column(nullable = false)
private String userEnum;
}
- Controller
@Autowired
UserService userService;
@PostMapping("user_login")
@ResponseBody
public boolean UserLoginResponse(@RequestBody Object object) {
Map<String, String> map = (Map<String, String>) object;
User user = new User();
user.setId(map.get("id"));
user.setPw(map.get("pw"));
user.setPhoneNum(map.get("phone"));
user.setNickname(map.get("nickname"));
user.setUserEnum(map.get("userEnum"));
return userService.userLogin(user);
}
- Service
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public Boolean userLogin(User user) {
// 소셜 로그인일 때 : 가입이 안되있다면, 로그인 하면서 가입까지 같이 처리
if (!user.getUserEnum().toString().equals("LoginUser")) {
if (userRepository.findById(user.getId()).isEmpty()) {
userRepository.save(user);
}
return true;
}
//일반 로그인일 떄 :
else {
List<User> all = userRepository.findAll();
for (User userList : all) {
// 아이디와 비밀번호가 모두 일치할 떄.
if (userList.getId().equals(user.getId()) && userList.getPw().equals(user.getPw())) {
return true;
}
}
return false;
}
}
}
*P.S : 현재 이 코드에서는 소셜 로그인와 일반 로그인이 같은 URL로 처리되어 있기 때문에 혼란스러울 수 있는데, else 부분을 보면 된다.또 Controller에서 id, pw 뿐만 아니라 다른 부분까지 세팅하고 있는데, 이는 소셜 로그인과 일반 로그인을 같은 URL로 사용하고 있기 때문에, id와 pw부분만 보면 이해가 쉽다.