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
65 changes: 65 additions & 0 deletions keyword/10주차 키워드 정리.md
Original file line number Diff line number Diff line change
@@ -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는 이 이벤트를 다른 컴포넌트에 전달하거나 행동을 지시함.
1 change: 1 addition & 0 deletions mission/week3/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -12,13 +15,16 @@ 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() {

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)
Expand All @@ -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)
Expand Down Expand Up @@ -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}")
}

}
}
}
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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) {
Expand Down
9 changes: 6 additions & 3 deletions mission/week3/app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,27 @@
>

<LinearLayout
android:layout_width="wrap_content"
android:id="@+id/main_mini_player_to_song_ll"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/main_mini_player_btn_ll"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">

<TextView
android:id="@+id/main_mini_player_title_tv"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="13sp"
android:text="LILAC"
android:textColor="@color/black" />

<TextView
android:id="@+id/main_mini_player_singer_tv"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"

Expand All @@ -74,6 +76,7 @@
</LinearLayout>

<LinearLayout
android:id="@+id/main_mini_player_btn_ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
Expand Down
2 changes: 2 additions & 0 deletions mission/week3/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ activity = "1.10.1"
constraintlayout = "2.2.1"
circleindicator = "2.1.6"
gson = "2.13.1"
fragmentKtx = "1.8.8"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
Expand All @@ -23,6 +24,7 @@ androidx-activity = { group = "androidx.activity", name = "activity", version.re
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
circleindicator = { group = "me.relex", name = "CircleIndicator", version.ref = "circleindicator" }
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Expand Down