diff --git "a/keyword/Chapter08/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" "b/keyword/Chapter08/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" index e69de29..a4b3c4a 100644 --- "a/keyword/Chapter08/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" +++ "b/keyword/Chapter08/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" @@ -0,0 +1,73 @@ +- OAuth2 방식 + - OAuth2란 무엇일까요? + + 사용자가 자신의 **비밀번호를 제공하지 않고도**, 제3자 앱이 자원 서버에 **제한된 접근 권한을 위임받을 수 있도록** 도와주는 **인증/인가 프로토콜** + + **인증을 외부 서비스(카카오, 구글 등)에 위임**하는 방식 + + + - 우리 주변에서 OAuth2 방식이 주로 사용되는 곳은 어디일까요? (Hint: 간편 로그인) + - **카오/네이버/구글/Apple로 간편 로그인** + - 외부 서비스가 **사용자의 Google Drive, Calendar, GitHub 등의 데이터를 접근**할 때 + - **Slack, Notion, Dropbox API 연동** 시 + - 토큰이란 무엇일까요? + - 인증이 완료되었음을 증명하는 **디지털 증표** + - 서버는 토큰을 통해 클라이언트의 권한을 검증할 수 있다. + - OAuth2에서 사용되는 토큰은 어떤 것이 있고, 각각 어떤 용도일까요? + 1. Access Token + - 리소스 접근에 사용되는 **주요 인증 수단** + - 유효 기간이 짧음 (보안 목적) + - API 호출 시 Authorization 헤더에 `Bearer {token}` 형식으로 전달 + 2. Refresh Token + - Access Token이 만료되었을 때, **새로운 Access Token을 발급받기 위한 토큰** + - 일반적으로 유효 기간이 더 김 + - 주로 서버 간 통신에 사용하며, **외부에 노출되면 보안상 위험** + - OAuth2 방식의 장점과 단점은 무엇일까요? + - 장점 + - **비밀번호 제공 없이** 외부 서비스에 인증 위임 가능 + - **보안 향상**: 토큰 기반, Scope 제한 가능 + - **유저 친화적**: 간편 로그인 구현이 쉬움 + - **표준화**되어 다양한 서비스에 적용 가능 + - 단점 + - 구현이 복잡할 수 있음 (Redirect, 토큰 저장, 갱신 등) + - 토큰 탈취 시 보안 이슈 발생 가능 + - Refresh Token 관리 책임이 클라이언트나 서버에 있음 +- Cookie와 Session + - Cookie란 무엇일까요? + - 클라이언트(브라우저)에 **작은 데이터 조각을 저장**하는 방법 + - 서버가 Set-Cookie 헤더를 통해 전달하며, 클라이언트는 이후 요청 시 함께 보냄 + - Cookie는 어떤 형식으로 이루어져 있나요? + - `key=value; Path=/; HttpOnly; Secure; Max-Age=3600` 등의 형식 + - 속성: `Domain`, `Path`, `HttpOnly`, `Secure`, `SameSite`, `Expires`, `Max-Age` 등 + - Session이란 무엇일까요? + - 서버가 **사용자의 상태 정보를 기억하는 방법** + - 서버는 사용자마다 고유한 `Session ID`를 생성하고, 서버 메모리(DB 등)에 데이터를 저장함 + - 클라이언트는 **Cookie를 통해 Session ID를 전송** + - Cookie와 Session을 사용하는 이유는 무엇일까요? + - HTTP는 **stateless**이기 때문에, 사용자의 로그인 상태나 장바구니 정보 등을 유지하려면 상태 관리가 필요 + - 이를 위해 Cookie와 Session을 조합해 사용 + - Cookie와 Session의 차이는 무엇일까요? + - +- JWT 방식 + - JWT (JSON Web Token)은 무엇일까요? + - 사용자 인증 정보를 담은 **자체 서명된 토큰 포맷** + - 서버는 토큰만 보고도 **사용자의 상태나 권한을 검증**할 수 있음 + - 주로 OAuth2의 **Access Token 형태**로 많이 사용됨 + - JWT 방식의 토큰은 어떤 구조로 되어있고, 각각 어떤 용도로 사용되나요? + 1. Header + 토큰의 타입(`JWT`)과 서명 알고리즘(`HS256`, `RS256` 등) + 2. Payload + - 사용자 정보 (ex: id, email, role 등) + - 만료 시간, 발급자 등 **클레임(claim)** 포함 + 3. Signature + - Header + Payload를 비밀 키로 서명한 값 + - 위변조 여부를 검증하는 데 사용됨 + - JWT 방식의 장점과 단점은 무엇일까요? + - 장점 + - **서버가 상태 정보를 기억할 필요 없음** (Stateless) + - **확장성 높음**: 여러 서버에서 공유 없이 인증 가능 + - **빠른 인증 처리**: DB 접근 없이 토큰만으로 사용자 식별 가능 + - 단점 + - **만료 전까지 취소 불가** (로그아웃 처리 어려움) + - **토큰 탈취 시 위험**: Signature 검증만으로는 권한 철회 불가 + - 토큰 길이가 김 (네트워크 부하 증가) \ No newline at end of file diff --git "a/keyword/Chapter09/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" "b/keyword/Chapter09/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" new file mode 100644 index 0000000..60a90e5 --- /dev/null +++ "b/keyword/Chapter09/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" @@ -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를 사용해야 합니다. + + + +- 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 객체로 변환하기 위한 클래스 \ No newline at end of file diff --git "a/keyword/Chapter10/MVC\355\214\250\355\204\264\353\266\204\354\204\235" "b/keyword/Chapter10/MVC\355\214\250\355\204\264\353\266\204\354\204\235" new file mode 100644 index 0000000..ecc32b7 --- /dev/null +++ "b/keyword/Chapter10/MVC\355\214\250\355\204\264\353\266\204\354\204\235" @@ -0,0 +1,15 @@ +전체적으로 기능별로 구분이 되어있는것으로 추정된다. + +1. UI + + 화면별 기능 분리 + + 메인과 signup등 액티비티별로 모듈화 + +2. Util + + spf, ViewUtil등 앱 전반에서 사용되는 기능들 + +3. Data + + 다양한 데이터클래스들 \ No newline at end of file diff --git "a/keyword/Chapter10/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" "b/keyword/Chapter10/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" new file mode 100644 index 0000000..5209763 --- /dev/null +++ "b/keyword/Chapter10/\355\202\244\354\233\214\353\223\234\354\240\225\353\246\254.md" @@ -0,0 +1,86 @@ +- RESTful API + - REST(Representational State Transfer)란 무엇일까요? + + **웹 서비스가 어떻게 동작해야하는지에 대한 아키텍쳐 설계원칙** + + 클라이언트와 서버간 상호작용을 규정하고, 여러가지 원칙과 제약조건을 갖는다 + + - 서버 클라이언트 구조 + - Stateless : 클라이언트의 정보를 서버에 저장하지 않는다. + - Caxcheable: HTTP프로토콜을 그대로 사용하므로 웹의 인프라 사용가능 → 캐싱도 가능 + - LayeredSystem : REST 서버는 계층적으로 구성될 수 있다. 그러나 클라이언트는 **`REST API Server`**만 호출한다 + - Code-On-Demand : Server로부터 스크립트를 받아서 Client에서 실행한다. + - Uniform Interface(인터페이스 일관성) : URI로 지정한 Resource에 대한 조작을 통일되고 한정적인 인터페이스로 수행 + + - REST API의 구성 요소에는 무엇이 있고, 각각 무슨 역할을 할까요? + 1. 자원 (Resource) : **URI** + - 모든 자원에 고유한 ID가 존재하고, 이 자원은 Server에 존재한다. + - 자원을 구별하는 ID는 ‘/groups/:group_id’와 같은 HTTP URI 다. + - Client는 URI를 이용해서 자원을 지정하고 해당 자원의 상태(정보)에 대한 조작을 Server에 요청한다. + 2. 행위 (Verb) : **HTTP Method** + - HTTP 프로토콜의 Method를 사용한다. + - HTTP 프로토콜은 GET, POST, PUT, DELETE 와 같은 메서드를 제공한다. + 3. 표현 (Representation) : + - Client가 자원의 상태(정보)에 대한 조작을 요청하면 Server는 이에 적절한 응답(Representation)을 보낸다. + - REST에서 하나의 자원은 JSON, XML, TEXT, RSS 등 여러 형태의 Representation으로 나타내어 질 수 있다. + - JSON 혹은 XML를 통해 데이터를 주고 받는 것이 일반적이다. + + + + - RESTful API란 무엇일까요? + + RESTful API는 REST 원칙을 따르는 API로, 자원을 URI로 명확히 표현하고 HTTP 메서드를 통해 자원에 대한 동작을 수행합니다. + + 서버는 상태를 저장하지 않으며(stateless), 클라이언트 요청마다 완전한 정보를 포함해야 합니다. + + - RESTful API에서 사용하는 중요한 개념 + - HTTP Method는 무엇일까요? + + HTTP 메서드는 자원에 대해 수행할 작업을 나타내며, 다음과 같은 메서드가 자주 사용됩니다: + + - **GET**: 자원 조회 + - **POST**: 자원 생성 + - **PUT**: 자원 전체 수정 + - **PATCH**: 자원 일부 수정 + - **DELETE**: 자원 삭제 + - HTTP Status Code는 무엇일까요? + + HTTP 상태 코드는 클라이언트 요청에 대한 서버의 응답 결과를 숫자로 표현한 코드입니다. + + 클라이언트는 이 코드를 통해 요청이 성공했는지, 실패했는지, 추가 조치가 필요한지 판단할 수 있습니다. + + - HTTP Status Code의 종류에는 무엇이 있을까요? + + + | **Code** | **언제 나타날까요??** | + | --- | --- | + | **1xx** | 요청이 수신되었으며 계속 진행 중임 | + | **2xx** | 요청이 성공적으로 처리됨 | + | **3xx** | 리다이렉션 필요 | + | **4xx** | 클라이언트 오류 | + | **5xx** | 서버 오류 | +- Paging + - Paging이란 무엇일까요? + Paging은 대량의 데이터를 클라이언트에 **조금씩 나눠서 전송하는 기술**로, 서버와 클라이언트의 부담을 줄이기 위해 사용 + - Paging의 예시에는 무엇이 있을까요? + - 블로그 게시물 목록: `/posts?page=2&size=10` + - 쇼핑몰 상품 목록: `/products?page=1&limit=20` + - 커뮤니티 댓글 목록: `/comments?postId=4&cursor=103` + - Paging이 필요한 이유는 무엇일까요? + - **성능 개선**: 서버/DB가 한 번에 많은 데이터를 처리하지 않아도 됨 + - **사용자 경험 향상**: 앱 또는 웹 화면에서 필요한 만큼만 불러오므로 빠르게 로딩 가능 + - **네트워크 효율성**: 전송량이 줄어 데이터 소비 절감 +- 디자인 패턴 + - 대표적인 디자인 패턴에는 무엇이 있고 어떤 방식으로 작동하나요? + 1. MVC (Model-View-Controller) + - **Model**: 비즈니스 로직과 데이터 처리 담당 (ex. DB, 데이터 가공) + - **View**: 사용자에게 보여지는 UI 구성 요소 + - **Controller**: 사용자 입력을 받아 Model과 View 사이를 연결 + 2. MVP (Model-View-Presenter) + - **Model**: 데이터 처리 로직 + - **View**: 사용자 UI (Activity, Fragment 등) + - **Presenter**: 모든 로직을 담당하며 View와 Model 사이의 중재자 역할 + 3. MVVM (Model-View-ViewModel) + - **Model**: 비즈니스 로직과 데이터 + - **View**: 사용자 UI + - **ViewModel**: UI 데이터를 가공하여 View에 전달하며, View는 ViewModel을 관찰만 함 \ No newline at end of file diff --git a/mission/Flo/app/src/main/java/com/example/flo/NetWork/AuthResponse.kt b/mission/Flo/app/src/main/java/com/example/flo/NetWork/AuthResponse.kt new file mode 100644 index 0000000..39d565c --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/NetWork/AuthResponse.kt @@ -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? + +) + + diff --git a/mission/Flo/app/src/main/java/com/example/flo/NetWork/AuthRetrofitInterface.kt b/mission/Flo/app/src/main/java/com/example/flo/NetWork/AuthRetrofitInterface.kt new file mode 100644 index 0000000..8f7be35 --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/NetWork/AuthRetrofitInterface.kt @@ -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 + + @POST("/users/login") + fun login(@Body user: User): Call + +} \ No newline at end of file diff --git a/mission/Flo/app/src/main/java/com/example/flo/NetWork/AuthService.kt b/mission/Flo/app/src/main/java/com/example/flo/NetWork/AuthService.kt new file mode 100644 index 0000000..b6f1642 --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/NetWork/AuthService.kt @@ -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 { + override fun onResponse(call: Call, response: Response) { + 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, t: Throwable) { + //실패처리 + } + }) + } + + + fun login(user: User) { + val loginService = getRetrofit().create(AuthRetrofitInterface::class.java) + + + loginService.login(user).enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + 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, t: Throwable) { + //실패처리 + } + }) + } +} \ No newline at end of file diff --git a/mission/Flo/app/src/main/java/com/example/flo/NetWork/NetworkModule.kt b/mission/Flo/app/src/main/java/com/example/flo/NetWork/NetworkModule.kt new file mode 100644 index 0000000..d636361 --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/NetWork/NetworkModule.kt @@ -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 +} \ No newline at end of file diff --git a/mission/Flo/app/src/main/java/com/example/flo/NetWork/Result.kt b/mission/Flo/app/src/main/java/com/example/flo/NetWork/Result.kt new file mode 100644 index 0000000..7056c92 --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/NetWork/Result.kt @@ -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 +) diff --git a/mission/Flo/app/src/main/java/com/example/flo/data/User.kt b/mission/Flo/app/src/main/java/com/example/flo/data/User.kt new file mode 100644 index 0000000..534309e --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/data/User.kt @@ -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 +} diff --git a/mission/Flo/app/src/main/java/com/example/flo/ui/login/LoginActivity.kt b/mission/Flo/app/src/main/java/com/example/flo/ui/login/LoginActivity.kt new file mode 100644 index 0000000..22aa6eb --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/ui/login/LoginActivity.kt @@ -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() { + } +} \ No newline at end of file diff --git a/mission/Flo/app/src/main/java/com/example/flo/ui/login/LoginView.kt b/mission/Flo/app/src/main/java/com/example/flo/ui/login/LoginView.kt new file mode 100644 index 0000000..4cf7a93 --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/ui/login/LoginView.kt @@ -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() +} \ No newline at end of file diff --git a/mission/Flo/app/src/main/java/com/example/flo/ui/notifications/SearchFragment.kt b/mission/Flo/app/src/main/java/com/example/flo/ui/notifications/SearchFragment.kt new file mode 100644 index 0000000..e5f03e7 --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/ui/notifications/SearchFragment.kt @@ -0,0 +1,29 @@ +package com.example.flo.ui.notifications + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.flo.databinding.FragmentSearchBinding + +class SearchFragment : Fragment() { + + private var _binding: FragmentSearchBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentSearchBinding.inflate(inflater, container, false) + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/mission/Flo/app/src/main/java/com/example/flo/ui/siginup/SignUpActivity.kt b/mission/Flo/app/src/main/java/com/example/flo/ui/siginup/SignUpActivity.kt new file mode 100644 index 0000000..8c66f3f --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/ui/siginup/SignUpActivity.kt @@ -0,0 +1,70 @@ +package com.example.udemy_android_template.ui.siginup + +import android.os.Bundle +import android.util.Log + +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.example.flo.NetWork.AuthService +import com.example.flo.data.User +import com.example.flo.databinding.ActivitySignupBinding + + +class SignUpActivity : AppCompatActivity() , SignUpView{ + + lateinit var binding: ActivitySignupBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySignupBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.signUpSignUpBtn.setOnClickListener { + signUp() + } + } + + private fun getUser(): User { + val email: String = + binding.signUpIdEt.text.toString() + "@" + binding.signUpDirectInputEt.text.toString() + val name: String = binding.signUpNameEt.text.toString() + val pwd: String = binding.signUpPasswordEt.text.toString() + + return User(email, pwd, name) + } + + + + private fun signUp() { + if (binding.signUpIdEt.text.toString() + .isEmpty() || binding.signUpDirectInputEt.text.toString().isEmpty() + ) { + Toast.makeText(this, "이메일 형식이 잘못되었습니다.", Toast.LENGTH_SHORT).show() + return + } + + if (binding.signUpPasswordEt.text.toString() != binding.signUpPasswordCheckEt.text.toString()) { + Toast.makeText(this, "비밀번호가 일치하지 않습니다.", Toast.LENGTH_SHORT).show() + return + } + + val authService = AuthService() + authService.setSignUpView(this) + + authService.signUp(getUser()) + + Log.d("SIGNUP-ACT/ASYNC", "Hello, FLO") + } + + override fun onSignUpLoading() { + TODO("Not yet implemented") + } + + override fun onSignUpSuccess() { + finish() + } + + override fun onSignUpFailure() { + //실패처리 + } +} \ No newline at end of file diff --git a/mission/Flo/app/src/main/java/com/example/flo/ui/siginup/SignUpView.kt b/mission/Flo/app/src/main/java/com/example/flo/ui/siginup/SignUpView.kt new file mode 100644 index 0000000..4b3a5cc --- /dev/null +++ b/mission/Flo/app/src/main/java/com/example/flo/ui/siginup/SignUpView.kt @@ -0,0 +1,8 @@ +package com.example.udemy_android_template.ui.siginup + +interface SignUpView { + fun onSignUpLoading() + fun onSignUpSuccess() + fun onSignUpFailure() + //fun onSignUpFailure(code: Int, message: String) +} \ No newline at end of file diff --git a/mission/Flo/app/src/main/res/drawable/ic_back.png b/mission/Flo/app/src/main/res/drawable/ic_back.png new file mode 100644 index 0000000..aafa5a6 Binary files /dev/null and b/mission/Flo/app/src/main/res/drawable/ic_back.png differ diff --git a/mission/Flo/app/src/main/res/drawable/selector_navi_home.xml b/mission/Flo/app/src/main/res/drawable/selector_navi_home.xml new file mode 100644 index 0000000..3ed18be --- /dev/null +++ b/mission/Flo/app/src/main/res/drawable/selector_navi_home.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mission/Flo/app/src/main/res/drawable/selector_navi_match.xml b/mission/Flo/app/src/main/res/drawable/selector_navi_match.xml new file mode 100644 index 0000000..f32483d --- /dev/null +++ b/mission/Flo/app/src/main/res/drawable/selector_navi_match.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mission/Flo/app/src/main/res/drawable/selector_navi_mypage.xml b/mission/Flo/app/src/main/res/drawable/selector_navi_mypage.xml new file mode 100644 index 0000000..3bad2a1 --- /dev/null +++ b/mission/Flo/app/src/main/res/drawable/selector_navi_mypage.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mission/Flo/app/src/main/res/drawable/selector_navi_search.xml b/mission/Flo/app/src/main/res/drawable/selector_navi_search.xml new file mode 100644 index 0000000..34fa8a2 --- /dev/null +++ b/mission/Flo/app/src/main/res/drawable/selector_navi_search.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mission/Flo/app/src/main/res/drawable/udemy_softsquard.png b/mission/Flo/app/src/main/res/drawable/udemy_softsquard.png new file mode 100644 index 0000000..5339fdd Binary files /dev/null and b/mission/Flo/app/src/main/res/drawable/udemy_softsquard.png differ diff --git a/mission/Flo/app/src/main/res/layout/activity_login.xml b/mission/Flo/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..c9167a0 --- /dev/null +++ b/mission/Flo/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mission/Flo/app/src/main/res/layout/activity_signup.xml b/mission/Flo/app/src/main/res/layout/activity_signup.xml new file mode 100644 index 0000000..659207d --- /dev/null +++ b/mission/Flo/app/src/main/res/layout/activity_signup.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mission/Flo/app/src/main/res/layout/fragment_search.xml b/mission/Flo/app/src/main/res/layout/fragment_search.xml new file mode 100644 index 0000000..b842786 --- /dev/null +++ b/mission/Flo/app/src/main/res/layout/fragment_search.xml @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file