2026/04/17

오늘의 이야기

MutableLiveData에서 MutableStateFlow로 전환하기


앱 만들기 이미지



 


안녕하세요, 개발자 여러분! 오늘은 Android 개발에서 MutableLiveData를 MutableStateFlow로 전환하는 방법에 대해 알아보겠습니다. StateFlow는 Kotlin의 코루틴을 활용한 상태 관리 도구로, LiveData보다 더 많은 장점을 제공합니다. 그럼 시작해볼까요?


왜 MutableStateFlow로 전환해야 할까요?



  1. Null 안전성: LiveData는 null 값을 허용하지만, StateFlow는 초기 값을 필요로 하므로 null 안전성을 보장합니다.

  2. 수명 주기 독립성: LiveData는 UI 컴포넌트의 수명 주기에 의존하지만, StateFlow는 코루틴 스코프 내에서 동작하므로 더 유연합니다.

  3. 스레드 안전성: StateFlow는 스레드 안전성을 제공하여 동시성 문제를 줄여줍니다.

  4. 플랫폼 독립성: StateFlow는 Kotlin Multiplatform을 지원하여 다양한 플랫폼에서 사용할 수 있습니다.


MutableLiveData에서 MutableStateFlow로 전환하기


기존의 MutableLiveData 코드를 MutableStateFlow로 전환하는 방법을 단계별로 설명하겠습니다.


기존 코드



kotlin



var _jobWantLists = MutableLiveData<List<JobWantList>>()
val jobWantLists: LiveData<List<JobWantList>> = _jobWantLists




변환된 코드



kotlin



import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

var _jobWantLists = MutableStateFlow<List<JobWantList>?>(null)
val jobWantLists: StateFlow<List<JobWantList>?> = _jobWantLists




위 코드에서는 MutableLiveData를 MutableStateFlow로 변경하였습니다. MutableStateFlow는 초기 값을 필요로 하므로 null을 초기 값으로 설정했습니다.


StateFlow를 Compose에서 관찰하기


Compose에서 StateFlow를 관찰하려면 collectAsState를 사용할 수 있습니다. 다음은 그 예시입니다.



kotlin



import androidx.compose.runtime.*
import kotlinx.coroutines.flow.collectAsState

@Composable
fun JobWantListScreen(viewModel: JobWantListViewModel) {
val jobWantLists by viewModel.jobWantLists.collectAsState()

jobWantLists?.let {
// jobWantLists 데이터를 사용하여 UI를 구성
} ?: run {
// 데이터가 없을 때의 처리
}
}




위 코드에서는 collectAsState를 사용하여 StateFlow를 Compose에서 상태로 관찰합니다. 그런 다음 jobWantLists 데이터를 사용하여 UI를 구성합니다.


결론


MutableLiveData에서 MutableStateFlow로 전환하면 더 안전하고 유연한 상태 관리를 할 수 있습니다. StateFlow는 Kotlin의 코루틴을 활용하여 더 나은 성능과 확장성을 제공합니다. 여러분도 프로젝트에서 StateFlow를 사용해보세요!





오늘의 이야기

Jetpack Compose와 Retrofit을 활용한 번역기 앱 만들기


translate




1. 프로젝트 설정



먼저, Android Studio에서 새로운 프로젝트를 생성합니다. 그리고 build.gradle 파일에 필요한 종속성을 추가합니다.

Gradle


dependencies {
implementation "androidx.compose.ui:ui:1.0.5"
implementation "androidx.compose.material:material:1.0.5"
implementation "androidx.compose.ui:ui-tooling-preview:1.0.5"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
implementation "androidx.activity:activity-compose:1.3.1"
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.1"
}


2. AndroidManifest.xml 설정



인터넷 권한을 추가합니다.
XML


<uses-permission android:name="android.permission.INTERNET"/>


3. Retrofit 인터페이스 정의



Retrofit 인터페이스를 정의하여 Google Translate API를 호출하는 방법을 설정합니다.

Kotlin


package com.example.translator

import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST

interface TranslateService {
@Headers("Content-Type: application/json")
@POST("language/translate/v2")
fun translateText(@Body request: TranslateRequest): Call<TranslateResponse>
}

data class TranslateRequest(
val q: String,
val target: String
)

data class TranslateResponse(
val data: Data
)

data class Data(
val translations: List<Translation>
)

data class Translation(
val translatedText: String
)


4. Retrofit 인스턴스 생성



Retrofit 인스턴스를 생성하고, API 키를 HTTP 헤더에 포함시키기 위해 인터셉터를 설정합니다.

Kotlin


package com.example.translator

import okhttp3.Interceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitInstance {
private const val BASE_URL = "https://translation.googleapis.com/"
private const val API_KEY = "YOUR_API_KEY"

private val client = OkHttpClient.Builder()
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.header("Authorization", "Bearer $API_KEY")
val request = requestBuilder.build()
chain.proceed(request)
}
.build()

val api: TranslateService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TranslateService::class.java)
}
}


5. MainActivity.kt 파일 작성



Jetpack Compose를 사용하여 UI를 구성하고, Retrofit을 사용하여 번역 요청을 처리합니다.

Kotlin


package com.example.translator

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TranslatorApp()
}
}
}

@Composable
fun TranslatorApp() {
var inputText by remember { mutableStateOf("") }
var translatedText by remember { mutableStateOf("") }
val coroutineScope = rememberCoroutineScope()

Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
TextField(
value = inputText,
onValueChange = { inputText = it },
label = { Text("Enter text to translate") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = {
coroutineScope.launch {
translateText(inputText) { result ->
translatedText = result
}
}
},
modifier = Modifier.fillMaxWidth()
) {
Text("Translate")
}
Spacer(modifier = Modifier.height(16.dp))
Text(text = translatedText)
}
}

fun translateText(text: String, callback: (String) -> Unit) {
val request = TranslateRequest(q = text, target = "es")

RetrofitInstance.api.translateText(request).enqueue(object : Callback<TranslateResponse> {
override fun onResponse(call: Call<TranslateResponse>, response: Response<TranslateResponse>) {
if (response.isSuccessful) {
val translatedText = response.body()?.data?.translations?.firstOrNull()?.translatedText ?: "Translation failed"
callback(translatedText)
} else {
callback("Translation failed: ${response.message()}")
}
}

override fun onFailure(call: Call<TranslateResponse>, t: Throwable) {
callback("Translation failed: ${t.message}")
}
})
}


마무리
이 포스트에서는 Retrofit 인터셉터를 사용하여 Google Translate API 키를 안전하게 전달하는 방법과 Jetpack Compose를 사용하여 간단한 번역기 앱을 만드는 방법에 대해 설명드렸습니다. 이 예제를 기반으로 다양한 기능을 추가하여 더 복잡한 번역기 앱을 만들 수 있습니다. 예를 들어, 다양한 언어를 지원하거나 번역 기록을 저장하는 등의 기능을 추가할 수 있습니다.

이 포스트가 도움이 되셨길 바라며, 더 궁금한 점이 있다면 댓글로 남겨주세요! 행복한 코딩 되세요!





오늘의 이야기


#스하리1000명프로젝트,
Bazen yabancı işçilerle konuşmak zor oluyor, değil mi?
Yardımcı olacak basit bir uygulama yaptım! Siz kendi dilinizde yazarsınız ve başkaları da bunu kendi dillerinde görür.
Ayarlara göre otomatik çeviri yapar.
Kolay sohbetler için son derece kullanışlı. Fırsat bulduğunda bir göz at!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기

제트팩 구성에 펄스 효과 생성

펄스...




• 이 기사는 스케일, 알파, 브러시, 모양 및 애니메이션 사양을 제어하는 사용자 정의 수정자를 사용하여 안드로이드용 UI 도구 키트인 제트팩 컴포즈에서 펄스 효과를 생성하는 방법을 보여준다.

• ‘펄스 효과’ 수정자는 ‘리멤버 무한 전환’을 활용해 크기를 초기에서 목표 스케일로 애니메이션화하는 ‘펄스 스케일’과 투명도를 완전히 불투명에서 완전히 투명으로 애니메이션화하는 ‘펄스 알파’의 두 가지 애니메이션을 만들어 모두 무한 반복한다.

• ‘더블 펄스 효과’ 수식어도 제공돼 타이밍이 약간 다르고 색상이 잠재적으로 다른 두 개의 ‘펄스 효과’ 수식어를 레이어드해 시각적으로 더 매력적인 효과를 만들 수 있다.

• 이 기사는 '최소 상호작용 성분 크기' 수정자로 인해 버튼과 같은 상호작용 구성 요소에 펄스 효과를 적용할 때 잠재적인 문제를 강조하며 적절한 렌더링을 위해 구성 요소의 크기를 우선시할 것을 제안한다.

• 여러 예는 '필드아이콘버튼'과 '버튼'과 같은 다양한 제트팩 컴포즈 구성 요소에 적용된 '펄스이펙트'와 '더블펄스이펙트' 수정자를 보여주며 상태 변수를 사용한 옵션 디스플레이를 포함한 다양성과 사용자 정의 옵션을 보여준다.

https://medium.com/@kappdev/how-to-create-a-pulse-effect-in-jetpack-compose-265d49aad044

How to Create a Pulse Effect in Jetpack Compose

Welcome 👋 In this article, we’ll create an engaging Pulse Effect using Jetpack Compose. It’s a great way to capture the user’s attention…

medium.com





오늘의 이야기

안드로이드 개발과 유용한 팁들


 


메리크리스마스



 


안녕하세요, 오늘은 안드로이드 개발에 대한 유용한 팁과 예제를 공유하려고 합니다. 안드로이드 스튜디오 설정부터 자격 증명 관리, 그리고 Jetpack Compose를 활용한 BottomSheet 구현까지 다양한 주제를 다뤄봤습니다.


1. 안드로이드 스튜디오에서 ExampleCustomCredential 설정 방법



gradle



dependencies {
implementation 'com.example:custom-credential:1.0.0'
}




Gradle 파일에 위와 같이 설정을 추가하면 ExampleCustomCredential을 사용할 수 있습니다.


2. Credential Manager를 사용하여 사용자 로그인



java



CredentialManager credentialManager = CredentialManager.getInstance(context);
credentialManager.getCredentialPicker().startCredentialPicker(
CredentialPickerRequest.Builder.forPrompt("Sign In")
.build(),
CredentialRequest.forPassword("com.example.app", "password")
);




Credential Manager를 활용해 사용자를 로그인하는 예제입니다.


3. Jetpack Compose로 BottomSheet 구현 및 하단 고정



kotlin



val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()

ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
Column(
modifier = Modifier
.fillMaxWidth()
.height(300.dp) // 고정된 높이 설정
.verticalScroll(rememberScrollState()) // 스크롤 가능하게 설정
) {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
}
) {
// 메인 화면 내용
Button(onClick = {
scope.launch { sheetState.show() }
}) {
Text("Show BottomSheet")
}
}




Jetpack Compose에서 BottomSheet를 구현하고 고정된 높이 내에서 스크롤이 가능하게 설정하는 방법입니다.


4. jQuery를 사용하여 포커스 아웃 감지 및 문자열에서 숫자 추출



javascript



$(document).ready(function() {
$('#myInput').blur(function() {
alert('Input field has lost focus');
});

var str = "abc123def456";
var numbers = str.replace(/\D/g, '');
console.log(numbers); // "123456"
});




jQuery를 사용하여 input 텍스트 필드의 포커스 아웃을 감지하고, 문자열에서 숫자만 추출하는 방법입니다.


5. 여러 .docx 파일 병합 예제 (Java)



java



import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Body;

import java.io.File;

public class DocxMerger {
public static void main(String[] args) throws Docx4JException {
WordprocessingMLPackage doc1 = WordprocessingMLPackage.load(new File("path/to/first.docx"));
MainDocumentPart mainPart1 = doc1.getMainDocumentPart();

WordprocessingMLPackage doc2 = WordprocessingMLPackage.load(new File("path/to/second.docx"));
MainDocumentPart mainPart2 = doc2.getMainDocumentPart();

Body body1 = mainPart1.getJaxbElement().getBody();
Body body2 = mainPart2.getJaxbElement().getBody();

body1.getContent().addAll(body2.getContent());

doc1.save(new File("path/to/merged.docx"));

System.out.println("Document merged successfully!");
}
}




Java의 docx4j 라이브러리를 사용하여 여러 .docx 파일을 하나로 병합하는 방법입니다.


이상으로 오늘의 유용한 안드로이드 개발 팁들을 공유해드렸습니다. 더 궁금한 점이 있으시면 언제든지 질문해 주세요!





오늘의 이야기

2025년에도 다들 행복 하길...

happy new year






오늘의 이야기


#스하리1000명프로젝트,
Bị lạc ở Hàn Quốc? Ngay cả khi bạn không nói được tiếng Hàn, ứng dụng này vẫn giúp bạn đi lại dễ dàng.
Chỉ cần nói ngôn ngữ của bạn—nó sẽ dịch, tìm kiếm và hiển thị kết quả bằng ngôn ngữ của bạn.
Tuyệt vời cho du khách! Hỗ trợ hơn 10 ngôn ngữ bao gồm tiếng Anh, tiếng Nhật, tiếng Trung, tiếng Việt, v.v.
Hãy thử nó ngay bây giờ!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127




오늘의 이야기


#billcorea #운동동아리관리앱
🏸 Schneedle, badminton kulüplerinin olmazsa olmaz uygulaması!
👉 Maç Oyunu – Skorları Kaydedin ve Rakipleri Bulun 🎉
Tek başınıza, arkadaşlarınızla veya bir kulüpte her yerde mükemmel! 🤝
Badmintonu seviyorsanız mutlaka deneyin

Uygulamaya git 👉 https://play.google.com/store/apps/details?id=com.billcorea.matchplay




2026/04/16

오늘의 이야기

Kotlin 2.1.0의 새로운 기능 탐구


kotlin 2.1.0 update



 


안녕하세요, 개발자 여러분! 오늘은 Kotlin 2.1.0의 새로운 기능과 개선 사항에 대해 알아보겠습니다. Kotlin 2.1.0은 2024년 11월 27일에 출시되었으며, 언어의 표현력과 성능을 향상시키는 여러 새로운 기능을 도입했습니다. 최신 IntelliJ IDEA 2024.3 버전에서 K2 모드를 활성화하면 모든 기능을 사용할 수 있습니다.


1. when 표현식의 가드 조건


Kotlin 2.1.0 이전에는 중첩된 if 문이나 각 분기 내에서 명시적인 검사를 사용해야 했습니다. 이제 가드 조건을 사용하여 when 표현식의 분기에 여러 조건을 포함할 수 있습니다. 이를 통해 복잡한 제어 흐름을 더 명확하고 간결하게 만들 수 있습니다.


2. 비지역 break 및 continue


Kotlin 2.1.0 이전에는 람다 내에서 복잡한 루프를 처리하기 위해 레이블을 사용해야 했습니다. 이제 비지역 break 및 continue를 직접 사용할 수 있어 코드의 가독성을 높이고 보일러플레이트 코드를 줄일 수 있습니다.


3. 다중 달러 문자열 보간


Kotlin 2.1.0 이전에는 리터럴 달러 기호를 포함하는 문자열을 처리할 때 모든 인스턴스를 이스케이프해야 했습니다. 이제 다중 달러 보간 기능을 사용하면 보간을 트리거하는 달러 기호의 수를 구성할 수 있습니다.


4. K2 컴파일러 업데이트


Kotlin 2.1.0의 K2 컴파일러는 컴파일러 검사와 kapt 구현에 대한 유연성을 제공합니다. 이러한 업데이트는 성능을 향상시키고 컴파일 과정에 대한 더 많은 제어를 제공합니다.


5. Kotlin 멀티플랫폼 개선


Kotlin 2.1.0은 Swift 내보내기에 대한 기본 지원을 도입하여 Swift 코드와의 상호 운용성을 향상시킵니다. 또한, 멀티플랫폼 프로젝트에서 컴파일러 설정을 간소화하는 안정적인 Gradle DSL을 도입했습니다.


6. Kotlin/Native 개선


이번 릴리스는 iOS 애플리케이션 개발 경험을 향상시키는 iosArm64에 대한 개선 사항을 포함합니다. 성능 최적화 및 버그 수정도 포함되어 있어 더 안정적이고 효율적인 Kotlin/Native 환경을 제공합니다.


7. Kotlin/Wasm 업데이트


Kotlin 2.1.0은 Kotlin/Wasm에 대한 여러 업데이트를 포함하며, 특히 증분 컴파일 지원을 도입하여 빌드 시간을 단축하고 개발 워크플로를 개선합니다.


8. Gradle 지원 개선


이번 릴리스는 최신 Gradle 및 Android Gradle 플러그인 버전과의 호환성을 개선합니다. Kotlin Gradle 플러그인 API에 대한 업데이트는 개발자에게 더 많은 도구와 옵션을 제공합니다.


9. 문서 개선


Kotlin 문서에 대한 상당한 개선이 이루어져 개발자가 언어를 효과적으로 활용할 수 있도록 더 명확하고 포괄적인 가이드와 참조를 제공합니다.


10. Kotlin 2.1.0으로 업데이트


Kotlin 2.1.0을 사용하려면 빌드 스크립트에서 Kotlin 버전을 2.1.0으로 변경하면 됩니다. 2.1.0을 지원하는 Kotlin 플러그인은 최신 IntelliJ IDEA 및 Android Studio 버전에 번들로 포함되어 있어 추가 플러그인 업데이트가 필요하지 않습니다.


이 새로운 기능과 개선 사항을 탐구하고 채택함으로써 Kotlin 개발 경험을 향상시키고 언어의 최신 발전을 활용할 수 있습니다.


 


** 출처


Exploring Kotlin 2.1.0: New Features | by youssef moti | Jan, 2025 | Medium



 


Exploring Kotlin 2.1.0: New Features


Kotlin 2.1.0, released on November 27, 2024, introduces several new features and improvements that enhance the language’s expressiveness…


medium.com




 





오늘의 이야기

Python에서 문자열 유사도 검색 방법

문자열 유사도 검색은 두 문자열이 얼마나 유사한지를 판단하는 중요한 작업입니다. 이는 텍스트 마이닝, 자연어 처리, 데이터 정제 등 다양한 분야에서 사용됩니다. 이번 포스트에서는 Python을 사용하여 문자열 유사도를 측정하는 여러 가지 방법을 소개하고, 각 방법의 사용 예제를 제공하겠습니다.

## 1. Levenshtein Distance

**Levenshtein 거리**는 두 문자열 사이의 최소 편집 거리를 계산하는 방법입니다. 이 거리는 삽입, 삭제, 대체 등의 편집 연산을 통해 한 문자열을 다른 문자열로 변환하는 최소 연산 횟수를 의미합니다. `python-Levenshtein` 라이브러리를 사용하여 이를 구현할 수 있습니다.

```python
import Levenshtein

str1 = "hello"
str2 = "hallo"
distance = Levenshtein.distance(str1, str2)
print(f"Levenshtein Distance: {distance}")
```

위 예제에서는 "hello"와 "hallo" 사이의 Levenshtein 거리를 계산합니다.

## 2. SequenceMatcher (difflib)

Python의 표준 라이브러리인 `difflib`을 사용하여 문자열 간의 유사성을 비교할 수 있습니다. `SequenceMatcher` 클래스는 두 문자열 간의 유사성을 측정하는 편리한 방법을 제공합니다.

```python
from difflib import SequenceMatcher

str1 = "hello"
str2 = "hallo"
ratio = SequenceMatcher(None, str1, str2).ratio()
print(f"SequenceMatcher Ratio: {ratio}")
```

이 예제에서는 "hello"와 "hallo"의 유사도를 0에서 1 사이의 비율로 계산합니다.

## 3. Jaro-Winkler Similarity

**Jaro-Winkler 유사도**는 Levenshtein 거리와 유사하지만, 문자열의 접두어가 동일한 경우에 더 높은 유사도를 부여하는 알고리즘입니다. `jellyfish` 라이브러리를 사용하여 이를 구현할 수 있습니다.

```python
import jellyfish

str1 = "hello"
str2 = "hallo"
similarity = jellyfish.jaro_winkler(str1, str2)
print(f"Jaro-Winkler Similarity: {similarity}")
```

위 예제에서는 "hello"와 "hallo"의 Jaro-Winkler 유사도를 계산합니다.

## 4. Cosine Similarity

**Cosine 유사도**는 두 문자열을 벡터로 변환하고 벡터 간의 코사인 유사도를 계산하는 방법입니다. 이를 통해 문자열 간의 각도를 측정하고 유사도를 계산할 수 있습니다. `sklearn` 라이브러리를 사용하여 이를 구현할 수 있습니다.

```python
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

str1 = "hello"
str2 = "hallo"
documents = [str1, str2]

vectorizer = TfidfVectorizer().fit_transform(documents)
vectors = vectorizer.toarray()

similarity = cosine_similarity(vectors)
print(f"Cosine Similarity: {similarity[0][1]}")
```

이 예제에서는 TF-IDF 벡터화를 사용하여 "hello"와 "hallo"의 코사인 유사도를 계산합니다.

## 결론

이 포스트에서는 Python에서 문자열 유사도를 측정하는 네 가지 방법을 살펴보았습니다. 각 방법은 특정 상황에 따라 더 적합할 수 있으며, 사용 목적에 맞게 적절한 방법을 선택하여 사용할 수 있습니다. 문자열 유사도 검색은 텍스트 분석, 중복 제거, 정보 검색 등 다양한 분야에서 유용하게 사용될 수 있습니다.





오늘의 이야기


Java에서 Retrofit2와 함께 HTTP 요청을 처리하는 방법


디지털 노마드가 되자





지난 며칠간 우리는 Java에서 Retrofit2를 사용하여 HTTP 요청을 처리하는 다양한 방법에 대해 논의했습니다. Retrofit2는 네트워크 요청을 간단하게 만들기 위해 만들어진 강력한 HTTP 클라이언트 라이브러리입니다. 여기서는 Retrofit2의 사용법과 SSL 설정, 그리고 문제 해결 방법에 대해 정리해 보겠습니다.


1. Retrofit2 기본 설정


먼저, Retrofit2와 Gson 변환기를 사용하여 기본적인 HTTP 클라이언트를 설정하는 방법을 알아봅시다.



java



dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
}




Retrofit 인스턴스를 생성하고, API 호출을 초기화합니다.



java



import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {
private static final String BASE_URL = "https://api.example.com/";
private static Retrofit retrofit;

public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}

public static ApiService getApiService() {
return getRetrofitInstance().create(ApiService.class);
}
}




2. POST 요청 보내기


다음으로, Retrofit2를 사용하여 JSON 데이터를 POST 요청으로 전송하는 방법을 살펴보겠습니다.



java



public class User {
private String name;
private int age;
private String email;

// Getters and Setters
}




API 인터페이스를 정의합니다.



java



import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;

public interface ApiService {
@POST("users")
Call<UserResponse> createUser(@Body User user);
}




API를 호출하고, 응답을 처리하는 코드를 작성합니다.



java



import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class Main {
public static void main(String[] args) {
ApiService apiService = RetrofitClient.getApiService();

User user = new User();
user.setName("John Doe");
user.setAge(30);
user.setEmail("john.doe@example.com");

Call<UserResponse> call = apiService.createUser(user);
call.enqueue(new Callback<UserResponse>() {
@Override
public void onResponse(Call<UserResponse> call, Response<UserResponse> response) {
if (response.isSuccessful()) {
UserResponse userResponse = response.body();
System.out.println("User created: " + userResponse);
} else {
System.err.println("Request failed with code: " + response.code());
}
}

@Override
public void onFailure(Call<UserResponse> call, Throwable t) {
t.printStackTrace();
}
});
}
}




3. 로깅 인터셉터 설정


HTTP 요청 및 응답을 로깅하기 위해 HttpLoggingInterceptor를 설정합니다.



java



import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;

public class RetrofitClient {
private static final String BASE_URL = "https://api.example.com/";
private static Retrofit retrofit;

public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
// Logging Interceptor 설정
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

// OkHttpClient 설정
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build();

// Retrofit 인스턴스 생성
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}

public static ApiService getApiService() {
return getRetrofitInstance().create(ApiService.class);
}
}




4. SSL 설정


Retrofit2에서 SSL을 설정하여 안전한 연결을 설정하는 방법을 설명합니다.



java



import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import javax.net.ssl.*;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;

public class RetrofitClient {
private static final String BASE_URL = "https://your-secure-server.com/";
private static Retrofit retrofit;

public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
try {
// SSL 인증서 로드
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream certInputStream = new FileInputStream("path/to/your/certificate.crt");
Certificate ca = cf.generateCertificate(certInputStream);
certInputStream.close();

// 키스토어 생성 및 인증서 추가
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

// TrustManagerFactory 초기화
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);

// SSLContext 설정
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);

// OkHttpClient 설정
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) tmf.getTrustManagers()[0])
.hostnameVerifier((hostname, session) -> true)
.build();

// Retrofit 인스턴스 생성
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
} catch (Exception e) {
e.printStackTrace();
}
}
return retrofit;
}

public static ApiService getApiService() {
return getRetrofitInstance().create(ApiService.class);
}
}




5. 문제 해결


Retrofit을 사용할 때 발생할 수 있는 몇 가지 일반적인 문제와 그 해결 방법을 소개합니다.


문제: NoClassDefFoundError: kotlin/jvm/internal/Intrinsics



  • 해결 방법: Kotlin 런타임 라이브러리를 추가해야 합니다.



gradle



dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}




문제: java.lang.NoClassDefFoundError: okio/Buffer



  • 해결 방법: Okio 라이브러리를 추가해야 합니다.



gradle



dependencies {
implementation 'com.squareup.okio:okio:2.9.0'
}




이 글은 Java에서 Retrofit2를 사용하여 HTTP 요청을 처리하고 SSL을 설정하는 방법을 설명하였습니다. 문제 해결에 대한 팁도 포함되어 있어, 여러분의 프로젝트에 유용하게 사용될 수 있기를 바랍니다. 추가적인 질문이 있거나 도움이 필요하면 언제든지 댓글로 알려주세요! 😊




 


참조했던 jar 파일은 필요을 위해 묶어 둡니다.




retrofit-2.8.0.zip

2.84MB







오늘의 이야기

Jetpack Compose에서 알림 권한 요청과 알림 표시하기 Android 13(Tiramisu, API 33) 이상에서는 사용자가 알림을 수신하기 전에   POST_NOTIFICATIONS   권한을 요청해야 합니다. Jetpack Com...