Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions keyword/Chapter09/키워드정리.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
- 서버
- 서버란 무엇이고, 어떤 역할을 할까요?

서버(Server)는 클라이언트로부터 요청(Request)을 받아 이를 처리한 후 응답(Response)을 반환하는 컴퓨터 또는 프로그램입니다. 주로 데이터 저장, 처리, 공유 등의 역할을 합니다.

- 현실에서 서버와 유사한 역할을 하는 예시는 무엇이 있을까요?
- 도서관 사서

우리가 책(데이터)를 요청하면 사서(서버)는 책을 찾아서 전달해준다.

- 쿠팡

우라 물건(데이터)를 주문하면 쿠팡은 물건을 전해준다.

- 서버와 클라이언트
- 서버와 클라이언트는 일반적으로 1:1, 1:N, N:M 관계 중 어떤 관계를 가지고 있을까요?
- 위의 문장의 답을 찾고, 아래 문장을 완성해보세요.
- 서버는 다수의 클라이언트가 접근한다.
- 즉 서버와 클라이언트는 일반적으로 1:N의 관계다.
- 서버와 클라이언트의 통신 구조를 식당 예시와 연결하여 정리하고 스터디에서 본인이 정리한 예시를 공유해 보세요.
(식당에는 손님, 점원, 주방장, 냉장고가 있습니다.)
- 손님: 클라이언트 (요청자)
- 점원: API 서버 (중간에서 요청을 전달하고 응답을 다시 전달함)
- 주방장: 데이터베이스 서버 또는 내부 로직 처리자
- 냉장고: 저장된 데이터 (데이터베이스)

식당에서 손님이 점원에게 음식을 요청하면, 점원은 주방장에게 전달하고 요리가 완성되면 손님에게 전달합니다. 이와 유사하게 클라이언트는 서버에 요청을 보내고, 서버는 요청을 처리한 후 응답을 보냅니다.

- 서버와 클라이언트 간의 통신
- 서버와 클라이언트가 정보를 주고 받으려면 먼저 어떤 일을 해야할까요?
- 정보를 주고받기 위해서는 먼저 **서버와 클라이언트가 연결을 설정**해야 하며, 주로 URL과 포트를 기반으로 접속합니다.
- API 서버와 소통하기 위해 필요한 정보들을 담고있는 문서는 무엇일까요?
(Hint: ‘API’라는 단어와 ‘문서’라는 단어를 함께 검색해보세요!)

API 문서(API Documentation)

- 서버와 클라이언트 간의 통신 방식 - HTTP Protocol
- HTTP Protocol에서 정보를 주고받기 위한 중요한 구성요소 2가지는 무엇일까요?
- Request

클라이언트가 서버에 보내는 메시지로, 요청 방식(method), URL, 헤더(header), 본문(body) 등으로 구성됨

- Response

서버가 클라이언트의 요청에 대해 응답하는 메시지로, 상태 코드(status code), 헤더(header), 본문(body) 등을 포함


- HTTP 통신은 연결을 계속 유지하고 있을까요? 유지한다면 어떻게 유지할 수 있는지, 유지할 수 없다면 왜 유지할 수 없는지 찾아보세요. (Hint: State)

**HTTP 통신은 기본적으로 연결을 유지하지 않는다. (Stateless)**

서버는 클라이언트의 이전 요청 상태를 저장하지 않으며, 각 요청은 독립적으로 처리됩니다. 따라서 기본적으로 연결을 유지하지 않으며 상태 정보를 저장하려면 별도의 기술이 필요합니다. 연결 유지가 필요한 경우는 다음과 같이 해결할 수 있습니다.

- 쿠키(cookie)와 세션(session): 클라이언트 상태를 서버 또는 클라이언트에 저장
- 토큰 기반 인증(JWT 등): 매 요청마다 토큰을 헤더에 포함하여 상태 유지 대체
- HTTP persistent connection (Keep-Alive): 일정 시간 동안 동일 연결을 재사용
- HTTP Protocol에서는 Method를 통해 어떤 동작을 하고싶은지 나타낼 수 있습니다.
주요 Method 5가지는 무엇이 있을까요?
- GET: 리소스를 조회하기 위한 요청. 서버의 상태나 데이터를 변경하지 않음
- POST: 서버에 데이터를 생성(등록) 요청
- PUT: 리소스를 전체 수정
- DELETE: 리소스를 삭제
- PATCH: 리소스를 부분 수정
- 데이터를 주고받을 때 주로 사용하는 방식에 대해 알아보세요.
- Query Parameter (또는 Query String): URL에 `?key=value` 형식으로 붙는 데이터.

ex) `/users?name=John`

- Path Variable: URL 경로의 일부로서 식별자 등을 포함. 예: /users/123
- Body: 요청의 본문에 포함되는 데이터로, POST, PUT, PATCH 요청 시 주로 사용
- Header: 요청 또는 응답의 부가 정보. 인증 정보(Authorization), 콘텐츠 타입(Content-Type) 등이 포함됨
- HTTP에서 데이터를 주고받을 때는 서로 이해할 수 있는 특정 형식으로 진행해야 합니다.
아래의 형식들에 대해 조사해보세요.
- JSON(JavaScript Object Notation)

텍스트 기반의 경량 데이터 교환 형식으로 키-값 쌍으로 구성되어 사람이 읽기 쉬움

- XML(eXtensible Markup Language)

태그 기반의 데이터 표현 방식으로, 데이터 구조가 명확하지만 무겁고 복잡할 수 있음

- HTTPS란 무엇이고, HTTP와 어떤 차이가 있을까요?

**HTTP에 보안 계층(SSL/TLS)을 추가한 프로토콜**

. HTTP는 데이터를 암호화하지 않지만, HTTPS는 데이터를 암호화하여 네트워크 상에서 제3자에 의해 읽히거나 변조되지 않도록 보호합니다. 이 때문에 금융, 로그인 등 민감한 정보를 다루는 웹사이트는 HTTPS를 사용해야 합니다.

<aside>
💡

일반적으로 안드로이드 스튜디오는 HTTP를 통한 인터넷 연결을 막는다. 따라서 HTTP를 통해 연결을 시도할경우 해당 주소에 대한 예외처리가 필요하며, 테스트 용을 제외하고 권장되지 않는다.

</aside>

- API
- API란 무엇일까요? (Restful API가 무엇인지도 함께 정리해 주세요.)
- API(Application Programming Interface)

소프트웨어 간 상호작용을 위한 인터페이스로 ****외부 프로그램이 특정 기능을 사용할 수 있도록 함수, 명세 등을 제공한다.

- Restful API

HTTP 기반의 API 설계 원칙 중 하나로 자원의 URI를 통해 요청을 주고받으며, 상태(State)를 서버에 저장하지 않는다.


- Android에서 HTTP 통신하기
- Android에서 HTTP 통신을 위해서는 외부 라이브러리를 사용해야 편리하게 진행할 수 있습니다.
그 중 대표적인 라이브러리 Retrofit2에 대해서 찾아보세요.

Retrofit2는 Android에서 HTTP 통신을 쉽게 할 수 있도록 도와주는 라이브러리다. 인터페이스 기반으로 API 요청을 정의하며, Gson 등의 변환기를 통해 데이터를 객체로 쉽게 처리할 수 있다.

- Retrofit2를 실제로 Android App 내에서 사용하기 위해서 만들어야 하는 요소 3가지는 무엇이 있을까요?
- API 인터페이스 파일 (Service)
- 서버와 통신할 메서드를 정의하는 인터페이스
- 예: @GET("/users"), @POST("/login") 등
- Retrofit 객체 생성
- baseUrl, converter(GsonConverterFactory 등), OkHttpClient 등을 설정하여 인스턴스를 생성
- 데이터 모델 클래스
- 서버에서 주고받는 JSON 데이터를 Kotlin/Java 객체로 변환하기 위한 클래스
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.flo.NetWork

import com.google.gson.annotations.SerializedName

data class AuthResponse(
@SerializedName("isSuccess") val isSuccess: Boolean,
@SerializedName("code") val code: Int,
@SerializedName("message") val message: String,
@SerializedName("result") val result: Result?

)


Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.flo.NetWork

import com.example.flo.data.User
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST

interface AuthRetrofitInterface {
@POST("/users")
fun signUp(@Body user: User): Call<AuthResponse>

@POST("/users/login")
fun login(@Body user: User): Call<AuthResponse>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.example.flo.NetWork

import android.util.Log
import com.example.flo.data.User
import com.example.flo.ui.login.LoginView
import com.example.udemy_android_template.ui.siginup.SignUpView
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class AuthService {
private lateinit var signUpView: SignUpView
private lateinit var loginView: LoginView

fun setSignUpView(signUpView: SignUpView) {
this.signUpView = signUpView
}

fun setLoginView(loginView: LoginView) {
this.loginView = loginView
}

fun signUp(user: User) {

val signUpService = getRetrofit().create(AuthRetrofitInterface::class.java)

signUpService.signUp(user).enqueue(object : Callback<AuthResponse> {
override fun onResponse(call: Call<AuthResponse>, response: Response<AuthResponse>) {
if (response.isSuccessful && response.code() == 200) {
val signUpResponse: AuthResponse = response.body()!!

Log.d("SIGNUP-RESPONSE", signUpResponse.toString())

when (val code = signUpResponse.code) {
1000 -> signUpView.onSignUpSuccess()
2016, 2017 -> {
signUpView.onSignUpFailure()
}
}
}
}

override fun onFailure(call: Call<AuthResponse>, t: Throwable) {
//실패처리
}
})
}


fun login(user: User) {
val loginService = getRetrofit().create(AuthRetrofitInterface::class.java)


loginService.login(user).enqueue(object : Callback<AuthResponse> {
override fun onResponse(call: Call<AuthResponse>, response: Response<AuthResponse>) {
if (response.isSuccessful && response.code() == 200) {
val loginResponse: AuthResponse = response.body()!!

when (val code = loginResponse.code) {
1000 -> loginView.onLoginSuccess(code,loginResponse.result!! )
else -> loginView.onLoginFailure()
}
}
}

override fun onFailure(call: Call<AuthResponse>, t: Throwable) {
//실패처리
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.flo.NetWork


import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

const val BASE_URL = "http://3.35.121.185"

fun getRetrofit(): Retrofit {

val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()

return retrofit
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.flo.NetWork

import com.google.gson.annotations.SerializedName

data class Result(
@SerializedName("userIdx") val userIdx: Int,
@SerializedName("jwt") val jwt: String
)
18 changes: 18 additions & 0 deletions mission/Flo/app/src/main/java/com/example/flo/data/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.example.flo.data

import androidx.room.Entity
import androidx.room.PrimaryKey
import com.google.gson.annotations.SerializedName

@Entity(tableName = "UserTable")
data class User(
@SerializedName(value = "email")
var email : String,
@SerializedName(value = "password")
var password : String,
@SerializedName(value = "name")
var name : String
){
@PrimaryKey(autoGenerate = true)
val id : Int = 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.example.udemy_android_template.ui.login

import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.flo.MainActivity
import com.example.flo.NetWork.AuthService
import com.example.flo.NetWork.Result
import com.example.flo.ui.login.LoginView
import com.example.flo.data.User
import com.example.flo.databinding.ActivityLoginBinding
import com.example.udemy_android_template.ui.siginup.SignUpActivity


class LoginActivity : AppCompatActivity(), LoginView {
lateinit var binding: ActivityLoginBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)

binding.loginSignUpTv.setOnClickListener {
startActivity(Intent(this, SignUpActivity::class.java))
}

binding.loginSignInBtn.setOnClickListener {
login()
}
}



private fun login() {
if (binding.loginIdEt.text.toString().isEmpty() || binding.loginDirectInputEt.text.toString().isEmpty()) {
Toast.makeText(this, "이메일을 입력해주세요.", Toast.LENGTH_SHORT).show()
return
}

if (binding.loginPasswordEt.text.toString().isEmpty()) {
Toast.makeText(this, "비밀번호를 입력해주세요.", Toast.LENGTH_SHORT).show()
return
}

val authService = AuthService()
authService.setLoginView(this)

authService.login(getUser())
}

private fun getUser(): User {
val email = binding.loginIdEt.text.toString() + "@" + binding.loginDirectInputEt.text.toString()
val password = binding.loginPasswordEt.text.toString()

return User(email = email, password = password, name = "")
}

private fun startMainActivity() {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}

private fun saveJwt(jwt: Int) {
val spf = getSharedPreferences("auth" , MODE_PRIVATE)
val editor = spf.edit()

editor.putInt("jwt", jwt)
editor.apply()
}

private fun saveJwt2(jwt: String) {
val spf = getSharedPreferences("auth2" , MODE_PRIVATE)
val editor = spf.edit()

editor.putString("jwt", jwt)
editor.apply()
}



override fun onLoginSuccess(code : Int, result : Result) {
when(code) {
1000 -> {
saveJwt2(result.jwt)
startMainActivity()

}
}
}

override fun onLoginFailure() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.flo.ui.login


import com.example.flo.NetWork.Result
interface LoginView {
fun onLoginSuccess(code : Int, result : Result)
fun onLoginFailure()
}
Loading