SW 개발 일반/아키텍처

(2) FSD의 핵심 계층과 역할

growdai1y 2025. 2. 10. 13:59

1. FSD란 무엇인가?

Feature-Sliced Design(FSD)은 프로젝트를 기능 중심으로 구조화하여 유지보수성과 확장성을 높이는 방법론입니다. 기존 계층 기반 아키텍처(MVVM, 클린 아키텍처)와 달리 기능 단위로 모듈을 나누고, 관련된 코드(UI, 비즈니스 로직, 데이터 등)를 한 곳에 배치하는 방식을 따릅니다. 이를 통해 코드의 응집도를 높이고, 기능 단위의 독립성을 유지할 수 있습니다.

 

FSD는 여러 계층으로 구성되며, 각 계층이 서로 긴밀히 연결됩니다. 각 계층이 어떻게 상호작용하는지를 이해하는 것이 중요합니다. 이제 FSD의 핵심 계층을 살펴보고, 하나의 기능을 중심으로 이 계층들이 어떻게 유기적으로 연결되는지 예제를 통해 설명하겠습니다.

 

FSD 구성
https://feature-sliced.design/kr/docs/get-started/overview


2. FSD의 핵심 계층

1) App Layer (앱 레이어)

앱 레이어는 애플리케이션의 진입점이며, 전체적인 설정과 글로벌 구성을 담당합니다. 앱이 시작될 때 필요한 초기 설정을 처리하며, Application 클래스, 네비게이션 설정, 의존성 주입(DI) 등의 요소가 이 레이어에 포함됩니다.

예를 들어, Koin을 사용하여 의존성을 주입하는 경우, 앱 레이어에서 이를 초기화할 수 있습니다.

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            modules(listOf(appModule, featureModule, networkModule))
        }
    }
}

이러한 초기화 과정이 있으면 애플리케이션이 실행될 때 필요한 의존성을 자동으로 주입받을 수 있습니다.


2) Pages Layer (페이지 레이어)

페이지 레이어는 사용자가 접근하는 주요 화면을 구성하는 역할을 합니다. 이 레이어는 여러 기능(Feature)과 위젯(Widget)을 조합하여 화면을 구성합니다. 페이지 레이어의 역할은 개별 기능을 조립하여 사용자가 상호작용할 수 있는 완전한 UI를 제공하는 것입니다.

예제에서는 로그인 페이지를 구성하는 코드를 보여줍니다.

@Composable
fun LoginPage(viewModel: LoginViewModel, navController: NavController) {
    val state by viewModel.uiState.collectAsState()
    
    Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
        Text(text = "Login", style = MaterialTheme.typography.h4)
        Spacer(modifier = Modifier.height(16.dp))
        CustomTextField(value = state.username, onValueChange = viewModel::onUsernameChange, label = "Username")
        CustomTextField(value = state.password, onValueChange = viewModel::onPasswordChange, label = "Password", isPassword = true)
        CustomButton(text = "Login", onClick = { viewModel.login(navController) })
        if (state.isLoading) {
            CircularProgressIndicator()
        } else if (state.errorMessage != null) {
            Text("Error: ${state.errorMessage}", color = Color.Red)
        }
    }
}

3) Features Layer (기능 레이어)

기능 레이어는 개별 기능을 독립적으로 관리하며, UI, 비즈니스 로직, 데이터 처리를 포함합니다. 예를 들어, 로그인 기능을 담당하는 LoginFeature는 ViewModel, UseCase, Repository 등을 포함할 수 있습니다.

class LoginViewModel(private val loginUseCase: LoginUseCase) : ViewModel() {
    private val _uiState = MutableStateFlow(LoginUiState())
    val uiState: StateFlow<LoginUiState> = _uiState
    
    fun login(navController: NavController) {
        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isLoading = true)
            val result = loginUseCase.execute(_uiState.value.username, _uiState.value.password)
            if (result.isSuccess) {
                navController.navigate("home")
            } else {
                _uiState.value = _uiState.value.copy(isLoading = false, errorMessage = result.exceptionOrNull()?.message)
            }
        }
    }
}

4) Widgets Layer (위젯 레이어)

위젯 레이어는 여러 기능에서 재사용할 수 있는 공통 UI 컴포넌트를 포함하는 레이어입니다. 예를 들어, 로그인 페이지에서 사용된 CustomButtonCustomTextField는 위젯 레이어에서 관리됩니다.

@Composable
fun CustomButton(text: String, onClick: () -> Unit) {
    Button(
        onClick = onClick,
        modifier = Modifier.fillMaxWidth().padding(8.dp)
    ) {
        Text(text, fontSize = 16.sp, fontWeight = FontWeight.Bold)
    }
}

@Composable
fun CustomTextField(value: String, onValueChange: (String) -> Unit, label: String, isPassword: Boolean = false) {
    TextField(
        value = value,
        onValueChange = onValueChange,
        label = { Text(label) },
        visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None,
        modifier = Modifier.fillMaxWidth().padding(8.dp)
    )
}

5) Shared Layer (공유 레이어)

공유 레이어는 여러 기능에서 공통으로 사용되는 유틸리티, API 클라이언트, 데이터 모델 등을 포함하는 레이어입니다. 예를 들어, Retrofit을 사용한 네트워크 클라이언트는 공유 레이어에서 관리할 수 있습니다.

object RetrofitClient {
    private val retrofit = Retrofit.Builder()
        .baseUrl("https://api.example.com")
        .addConverterFactory(GsonConverterFactory.create())
        .client(OkHttpClient.Builder().build())
        .build()
    
    val api: ApiService = retrofit.create(ApiService::class.java)
}

3. 결론

FSD의 각 계층을 살펴보고, 간단한 코드를 통해 각 계층의 역할을 설명했습니다. FSD는 기능 중심의 코드 구조를 제공하여 확장성과 유지보수성을 높이는 강력한 아키텍처 패턴입니다. 기존의 MVVM, 클린 아키텍처에서 명확하지 않았던 부분을 보완하면서도, 실무에서 적용하기 쉬운 가이드를 제공합니다.

 

이제 FSD의 각 계층의 역할을 이해했으니, 다음 글에서는 실제 프로젝트에서 FSD를 어떻게 적용할 수 있는지에 대해 살펴보겠습니다.

반응형

'SW 개발 일반 > 아키텍처' 카테고리의 다른 글

(1) FSD란 무엇인가?  (0) 2025.02.08
클린 아키텍처란 무엇인가?  (0) 2025.01.16