diff --git "a/keyword/10\354\243\274\354\260\250 \355\202\244\354\233\214\353\223\234 \354\240\225\353\246\254.md" "b/keyword/10\354\243\274\354\260\250 \355\202\244\354\233\214\353\223\234 \354\240\225\353\246\254.md" new file mode 100644 index 0000000..1ded6a8 --- /dev/null +++ "b/keyword/10\354\243\274\354\260\250 \355\202\244\354\233\214\353\223\234 \354\240\225\353\246\254.md" @@ -0,0 +1,65 @@ + +## RESTful API +- REST(Representational State Transfer)란 무엇일까요? + - REST API의 구성 요소에는 무엇이 있고, 각각 무슨 역할을 할까요? + 1. 자원 (Resource) : 서버에 존재하는 데이터, URI로 표현됨 + 2. 행동 (Verb) : 자원에 대해 어떤 동작을 할 것인지 HTTP 메서드로 지정함. + 3. 표현 (Representation) : 자원의 현재 상태를 전달하는 형식. 일반적으로 json이나 xml 포맷으로 클라이언트에 상태를 전달함. + - RESTful API란 무엇일까요? + - REST 원칙을 잘 따르는 API + - 클라이언트가 HTTP를 통해 서버의 자원에 접근하고 HTTP 메서드와 URI를 활용해 일관된 방식으로 동작함. + - Stateless, 계층형 구조, 캐싱, 일관된 인터페이스 + - RESTful API에서 사용하는 중요한 개념 + - HTTP Method는 무엇일까요? + - 클라이언트가 서버에게 요청하는 verb, 행동의 유형을 정의 + 1. GET: 자원 조회 + 2. POST: 자원 생성 + 3. PUT: 자원 전체 수정 + 4. PATCH: 자원 일부 수정 + 5. DELETE: 자원 삭제 + - HTTP Status Code는 무엇일까요? + - 요청에 대한 서버의 응답 결과를 숫자로 나타낸 코드 + - HTTP Status Code의 종류에는 무엇이 있을까요? + - 1xx: 정보 응답 + - 2xx: 성공 + - 3xx: 리다이렉션 + - 4xx: 클라이언트 오류 + - 5xx: 서버 오류 + +## Paging + - Paging이란 무엇일까요? + - 전체 데이터를 일정한 단위인 페이지로 나누어 클라이언트에 전송하는 기법. + - Paging의 예시에는 무엇이 있을까요? + - 웹사이트 게시판의 경우, 한 페이지에서 전체 게시물에 접근하는 대신 일정 개수의 게시글만 접근 가능함. + - 쇼핑몰 상품 목록의 경우, 일정 개수의 상품만을 목록으로 보여줌 + - 무한 스크롤시 스크롤을 내리면 자동으로 다음 페이지의 데이터를 받아옴 + - Paging이 필요한 이유는 무엇일까요? + - 성능 향상: 모든 데이터를 한번에 전송하는 것은 네트워크, 메모리, db에 부하가 큼. 필요한 데이터만 불러와 속도가 증가. + - 사용자 경험(UX) 개선: 적절한 양의 데이터로 나누어 보여주어 가독성이 향상됨. + - 서버 리소스 절약: 요청시마다 소량의 데이터만 처리하여 부하가 작음 + - 유지 보수와 확장성 향상: 대규모 서비스로 확장될 경우에도 데이터를 효율적으로 관리할 수 있음. +## 디자인 패턴 + - 대표적인 디자인 패턴에는 무엇이 있고 어떤 방식으로 작동하나요? + 1. ## MVC (Model-View-Controller) + - Model-View-Controller로 애플리케이션을 분리하는 구조. + - Model: 데이터와 비즈니스 로직 처리(db, 상태 관리) + - View: 사용자에게 보여지는 UI + - Controller: 사용자의 입력을 받고 처리 후 모델 또는 뷰에 전달 + - Controller가 사용자의 입력을 처리-Model에서 데이터를 조회하거나 갱신-결과를 View에 전달해 UI를 업데이트하는 방식. + + 2. ## MVVM (Model-View-ViewModel) + - Model-View-ViewModel로 어플리케이션을 분리하는 구조. + - Model: 데이터와 비즈니스 로직 처리 + - View: 사용자에게 보여지는 UI + - ViewModel: view와 model 사이 중개 역할 (상태 관리, 데이터 바인딩) + - ViewModel, 데이터 바인딩의 사용으로 view와 model 간 연결이 단순 + - View는 ViewModel에 바인딩됨 + - ViewModel은 Model을 호출함 + - ViewModel의 변경 사항은 자동으로 View에 반영됨 + + 3. ## Mediator (중재자 패턴) + - 객체 간의 복잡한 의존 관계를 중재자 객체 하나로 집중시켜 결합도롤 낮춤. + - Component: 서로 통신하지 않음. + - Mediator: Component 사이 모든 의사소통을 담당함 + - Component가 Mediator에게 이벤트를 전달 + - Mediator는 이 이벤트를 다른 컴포넌트에 전달하거나 행동을 지시함. \ No newline at end of file diff --git a/mission/week3/app/build.gradle.kts b/mission/week3/app/build.gradle.kts index 9262f97..6bbc393 100644 --- a/mission/week3/app/build.gradle.kts +++ b/mission/week3/app/build.gradle.kts @@ -48,6 +48,7 @@ dependencies { implementation(libs.androidx.activity) implementation(libs.androidx.constraintlayout) implementation(libs.gson) + implementation(libs.androidx.fragment.ktx) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/mission/week3/app/src/main/java/com/example/week3/ui/MainActivity.kt b/mission/week3/app/src/main/java/com/example/week3/ui/MainActivity.kt index 252d01d..5703411 100644 --- a/mission/week3/app/src/main/java/com/example/week3/ui/MainActivity.kt +++ b/mission/week3/app/src/main/java/com/example/week3/ui/MainActivity.kt @@ -1,8 +1,11 @@ package com.example.week3.ui +import android.annotation.SuppressLint import android.content.Intent +import android.media.MediaPlayer import android.os.Bundle import android.util.Log +import android.view.View import androidx.appcompat.app.AppCompatActivity import com.example.week3.R import com.example.week3.databinding.ActivityMainBinding @@ -12,6 +15,7 @@ import com.example.week3.ui.main.LockerFragment import com.example.week3.ui.main.LookFragment import com.example.week3.ui.main.SearchFragment import com.example.week3.ui.player.SongActivity +import com.example.week3.ui.player.SongActivity.Timer import com.google.gson.Gson class MainActivity : AppCompatActivity() { @@ -19,6 +23,8 @@ class MainActivity : AppCompatActivity() { lateinit var binding: ActivityMainBinding private val gson: Gson = Gson() private var song: Song = Song() + private var mediaPlayer: MediaPlayer? = null + lateinit var timer: Timer override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -28,10 +34,22 @@ class MainActivity : AppCompatActivity() { setupMainPlayerClickListener() initBottomNavigation() + startTimer() + + // 재생-정지 아이콘에 대한 클릭 리스너 + binding.mainMiniplayerBtn.setOnClickListener { + setPlayerStatus(true) + } + binding.mainPauseBtn.setOnClickListener { + setPlayerStatus(false) + } } private fun setupMainPlayerClickListener() { - binding.mainPlayerCl.setOnClickListener { + binding.mainMiniPlayerToSongLl.setOnClickListener { + timer.interrupt() + mediaPlayer?.release() // 미디어플레이어 리소스 해제 + mediaPlayer = null //startActivity(Intent(this, SongActivity::class.java)) val intent = Intent(this, SongActivity::class.java) intent.putExtra("title", song.title) @@ -103,6 +121,70 @@ class MainActivity : AppCompatActivity() { private fun setMiniPlayer(song: Song) { binding.mainMiniPlayerTitleTv.text = song.title binding.mainMiniPlayerSingerTv.text = song.singer - binding.mainSongProgressSb.progress = (song.second * 100000) / song.playTime + binding.mainSongProgressSb.progress = (song.second * 1000) / song.playTime + + mediaPlayer = MediaPlayer.create(this, song.music) + + setPlayerStatus(song.isPlaying) + } + + // 플레이어 재생-정지 버튼 설정 + private fun setPlayerStatus(isPlaying: Boolean) { + song.isPlaying = isPlaying + timer.isPlaying = isPlaying + + if (isPlaying) { + // 현 상태: 음악 재생중 (아이콘: 멈추기 버튼) - 이게 구현되어야 함 + // 기대 값: 음악 멈춤 (아이콘: 재생 버튼) + binding.mainMiniplayerBtn.visibility = View.GONE + binding.mainPauseBtn.visibility = View.VISIBLE + mediaPlayer?.start() + } else { + // 현 상태: 음악 멈춤 (아이콘: 재생 버튼) - 이게 구현되어야 함 + // 기대 값: 음악 재생 (아이콘: 멈추기 버튼) + binding.mainMiniplayerBtn.visibility = View.VISIBLE + binding.mainPauseBtn.visibility = View.GONE + if (mediaPlayer?.isPlaying == true) { // 없으면 오류 발생 가능성 있음. + mediaPlayer?.pause() + } + } + } + + private fun startTimer() { + timer = Timer(playTime = song.playTime, isPlaying = song.isPlaying) + timer.start() + } + + inner class Timer(private val playTime: Int, var isPlaying: Boolean = true): Thread() { + private var second: Int = song.second + private var mills: Float = second.times(1000f) + + @SuppressLint("DefaultLocale") + override fun run() { + super.run() + try { + while (true) { + if (second >= playTime) { + break + } + + if (isPlaying) { + sleep(50) + mills += 50 + + runOnUiThread { + binding.mainSongProgressSb.progress = (((mills) / playTime)*100).toInt() + } + + if (mills % 1000 == 0f) { + second++ + } + } + } + } catch (e: InterruptedException) { + Log.d("Song", "Thread 죽음: ${e.message}") + } + + } } } \ No newline at end of file diff --git a/mission/week3/app/src/main/java/com/example/week3/ui/album/DetailFragment.kt b/mission/week3/app/src/main/java/com/example/week3/ui/album/DetailFragment.kt index 80838ad..cfcb562 100644 --- a/mission/week3/app/src/main/java/com/example/week3/ui/album/DetailFragment.kt +++ b/mission/week3/app/src/main/java/com/example/week3/ui/album/DetailFragment.kt @@ -1,10 +1,10 @@ package com.example.week3.ui.album import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.Fragment import com.example.week3.databinding.FragmentDetailBinding class DetailFragment : Fragment() { diff --git a/mission/week3/app/src/main/java/com/example/week3/ui/player/SongActivity.kt b/mission/week3/app/src/main/java/com/example/week3/ui/player/SongActivity.kt index be9ce77..4907fd0 100644 --- a/mission/week3/app/src/main/java/com/example/week3/ui/player/SongActivity.kt +++ b/mission/week3/app/src/main/java/com/example/week3/ui/player/SongActivity.kt @@ -162,13 +162,12 @@ class SongActivity : AppCompatActivity() { } private fun startTimer() { - timer = Timer(playTime = song.playTime, isPlaying = song.isPlaying) + timer = Timer(playTime = song.playTime, second = song.second, isPlaying = song.isPlaying) timer.start() } - inner class Timer(private val playTime: Int, var isPlaying: Boolean = true): Thread() { - private var second: Int = 0 - private var mills: Float = 0f + inner class Timer(private val playTime: Int, private var second: Int, var isPlaying: Boolean = true): Thread() { + private var mills: Float = second.times(1000f) @SuppressLint("DefaultLocale") override fun run() { @@ -184,7 +183,7 @@ class SongActivity : AppCompatActivity() { mills += 50 runOnUiThread { - binding.songProgressSb.progress = ((mills / playTime)*100).toInt() + binding.songProgressSb.progress = (((mills) / playTime)*100).toInt() } if (mills % 1000 == 0f) { diff --git a/mission/week3/app/src/main/res/layout/activity_main.xml b/mission/week3/app/src/main/res/layout/activity_main.xml index 1fdf97d..6e5f6ef 100644 --- a/mission/week3/app/src/main/res/layout/activity_main.xml +++ b/mission/week3/app/src/main/res/layout/activity_main.xml @@ -47,17 +47,19 @@ >