앱에서 로그인 API 를 부르면 성공이 온다. 그다음 API 는 로그인이 필요하다고 했다. 방금 로그인했는데 왜.
Table of contents
Open Table of contents
증상 — 앱에서만 풀리는 로그인
순서대로 의심하고 하나씩 지웠다.
- 로그인이 사실 실패한 것 아닐까 → 응답 본문에 성공이라고 적혀 있었다
- 아이디를 잘못 넘기는 것 아닐까 → 로그를 찍어 보니 맞았다
- 서버가 이상한 것 아닐까 → 브라우저로 같은 순서를 밟으니 잘 됐다
Servlet 로그를 보니 HttpSession 이 매번 새로 생기고 있었다. JSESSIONID 가 계속 달랐다.
서버가 아니라 앱 쪽이었다. 브라우저로 되고 앱으로 안 되면 브라우저가 하는 일 중에 내가 안 하는 것이 있다는 뜻이다.
브라우저가 대신 해 주던 일이 있었다
개발자 도구로 로그인 요청과 그다음 요청을 나란히 열어 봤다. 헤더에 내가 안 보내는 것이 하나 있었다.
1. 로그인 요청
2. 서버 → 응답 헤더에 Set-Cookie: JSESSIONID=abc123
3. 다음 요청 → 헤더에 Cookie: JSESSIONID=abc123 을 실어 보내야 함
4. 서버는 그 값으로 "아까 그 사람"인 걸 안다
브라우저는 2번에서 받은 값을 저장했다가 3번에서 자동으로 붙인다. Android 에는 그걸 해 주는 것이 없었다. 받은 JSESSIONID 를 아무도 안 들고 있었다.
부를 때마다 새로 만들고 있었다
쿠키를 저장하는 코드도 없지만 지우는 코드도 없었다. 그럼 어디로 사라진 걸까. 로그인 부분과 조회 부분을 나란히 놓고 보다가 알았다.
// 로그인
HttpClient client = new DefaultHttpClient();
client.execute(loginPost);
// 내 정보 조회 — 다른 메서드
HttpClient client = new DefaultHttpClient(); // ← 여기
client.execute(infoGet);
DefaultHttpClient 는 안에 BasicCookieStore 를 들고 있다. 그런데 메서드마다 new DefaultHttpClient() 를 하고 있었다. 객체가 새로 생기면 CookieStore 도 빈다.
고치는 방법은 간단했다. 하나만 만들어 계속 쓰는 것이다.
public class ApiClient {
private static HttpClient client;
private static HttpClient get() {
if (client == null) {
client = new DefaultHttpClient();
}
return client;
}
}
같은 객체를 계속 쓰니 쿠키가 다음 요청에 자동으로 붙었다. 지금 쓰면 당연한 이야기인데 그때는 객체가 상태를 들고 있다는 감각이 없었다.
로그아웃에서 또 막혔다
로그아웃을 해도 로그인 상태가 유지됐다. 당연했다. 로그아웃은 서버 세션을 끊는 것인데 앱이 들고 있는 쿠키는 그대로였다.
public static void clearSession() {
((AbstractHttpClient) get()).getCookieStore().clear();
}
지금은 로그아웃 API 와 clear() 를 둘 다 한다. 한쪽만 해도 되는지는 모른다. 상태를 가진 물건은 만들 때만이 아니라 지울 때도 생각해야 한다는 걸 여기서 처음 알았다.
앱을 껐다 켜면 메모리가 사라지니 쿠키도 사라진다. SharedPreferences 에 JSESSIONID 를 넣는 예제를 봤는데 그대로 두면 다른 앱이 볼 수 있다고 한다. 암호화해야 하는지는 아직 모른다.
정리
- 브라우저로 되고 앱으로 안 되면 브라우저가 대신 해 주던 일을 찾는다
- 브라우저는
Set-Cookie를 저장했다가 다음 요청에 다시 보낸다 - Android 에는 그 일을 해 주는 것이 없다
DefaultHttpClient가BasicCookieStore를 들고 있어 그 역할을 한다- 요청마다 새로 만들면 쿠키가 그 객체와 함께 사라진다
JSESSIONID가 매번 다르면 서버가 아니라 클라이언트 쪽을 본다- 상태를 가진 물건은 만드는 쪽만 보다가 비우는 쪽에서 막힌다
- 앱을 껐다 켜도 유지하려면 어딘가 저장해야 하고 그때부터는 저장 위치가 문제가 된다