#widget

질문 2
해시태그 없이 키워드만 일치하는 질문은 개수에 포함되지 않아요.

2달 전 · 박준서 님의 새로운 댓글

리액트 프로젝트에 FSD 아키텍처 적용. 이 구성이 맞을까요?

안녕하세요. 현재 진행 중인 리액트 프로젝트에서 유지보수성과 확장성에 어려움을 느껴 폴더 구조를 Feature-Sliced Design(FSD) 아키텍처 기반으로 전면 리팩토링했습니다. 기존에는 `components/`, `pages/`, `apis/` 등 기능과 역할이 섞인 구조로 되어 있어, 코드의 위치가 불분명하고 협업에 어려움이 있었습니다. 이를 해결하고자 다양한 아키텍처를 조사한 끝에, FSD의 레이어 개념(Layers)에 맞춰 다음과 같은 방식으로 구조를 정리했습니다. - `app/`: `App.js`, `index.js` 등 프로젝트 진입점과 글로벌 설정 파일을 포함 - ` entities/`: `User`, `Article`, `CodingZone` 등 주요 도메인의 데이터 모델과 API 연동 담당 - `features/`: 로그인, 게시글 작성, 코딩존 출석 등 각 기능별로 모듈화하고, 필요한 경우 `hooks/` 등의 내부 디렉토리로 세분화 - `pages/`: 라우팅과 연결된 실제 페이지 컴포넌트 관리 (예: `CreatePage`, `EditPage` 등) - `widgets/`: 재사용 가능한 독립 UI 요소들 (예: `Footer`, `Navbar`, `Pagination` 등) - `shared/`: 공통 API, 유틸, 모달 컴포넌트 등 여러 기능에서 공유되는 요소들을 배치 기능 중심의 구조로 바꾸면서, 각 요소의 역할이 명확해지고 코드 탐색 및 유지보수가 훨씬 쉬워졌습니다. 현재는 복잡한 비즈니스 로직이 없어 `processes/` 레이어는 생략했지만, 추후 워크플로우가 필요한 기능이 생긴다면 도입할 계획입니다. 제가 구성한 이 폴더 구조와 레이어 분리가 실제 FSD 아키텍처 가이드에 부합하는 방향인지, 혹시 보완하거나 개선할 부분이 있다면 조언을 구하고 싶습니다. 자세한 내용은 블로그에 정리해 두었습니다. 👉 [https://juncci.tistory.com/4](https://juncci.tistory.com/4) 읽어주셔서 감사합니다!

개발자

#fsd

#react

#refactory

#프론드엔드

#폴더구조

답변 1

댓글 1

조회 167

일 년 전 · 익명 님의 새로운 답변

flutter 위젯 빌드 2번 호출되는 현상

안녕하십니까 flutter로 졸업 작품 앱 개발 중인 대학생입니다! 개발 도중 widget build가 2번씩 되는 현상이 발생합니다. build 내부에서 위젯이 return 되기 이전에 특정 함수가 호출되도록 했는데, 그 함수가 2번씩 호출됩니다. 로직에는 아무런 문제가 없구요, 혹시 몰라 디버깅 로그도 찍어봤는데 역시나 2번씩 찍힙니다. 함수가 페이지를 호출하는거라 사실상 같은 페이지 2개가 호출되는 문제라 타격이 큽니다.. 비슷한 문제를 해결한 경험이 있으신 분들의 조언을 듣고 싶습니다!

개발자

#flutter

#widget

#build

#route

답변 1

댓글 0

조회 155

일 년 전 · 민 님의 질문

플러터 질문. Futuer 및 출

locate.dart를 만들어서 import 'package:geolocator/geolocator.dart'; class Locate{ Future<List<double>> location() async { List<double> m_l = []; // 위치 권한 요청 LocationPermission permission = await Geolocator.requestPermission(); // 현재 위치 가져오기 Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high); // 위치 정보 리스트에 추가 m_l.add(position.longitude); m_l.add(position.latitude); return m_l; // 위치 정보를 포함한 리스트 반환 } Future<List<double>> get_loc() async { // 위치 정보를 가져오기 List<double> location = await this.location(); // 수정된 부분 return location; // 위치 정보를 포함한 리스트 반환 } } 를 작성하였구요 main_screen.dart 라는 파일에서 함수를 생성해서 my_locate() async{ Locate locator = Locate(); // 위치 정보 가져오기 List<double> location = await locator.get_loc(); } @override Widget build(BuildContext context) { // 함수 실행은 //요 부분에서 return Scaffold( 이 부분 안에서 실행시킬려고 하는데 자꾸 오류가 걸리네요. 방법 알려주실 분 있나요?

개발자

#flutter

#dart

답변 0

댓글 0

보충이 필요해요 1

조회 45

2년 전 · 정주연 님의 질문

Testflight widget

Testflight에서 widget 가능할까요?

개발자

#front

#testflight

답변 0

댓글 0

조회 38

2년 전 · 익명 님의 답변 업데이트

해당 스프링부트 코드 해석 좀 부탁드립니다..

안드로이드에쓸 스프링부트 코드를 따왔는데 제가 잘이해사 안되서 상세히 알려주실 선배님조언 구합니다 package com.example.teamproject import MyAdapter import android.content.Context import android.content.Intent import android.icu.lang.UCharacter.GraphemeClusterBreak.L import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import com.example.teamproject.databinding.ActivitySearchBinding import com.example.teamproject.login.LoginActivity import com.example.teamproject.model.RstrModel import com.example.teamproject.review.ReviewActivity import com.google.android.material.bottomnavigation.BottomNavigationView import retrofit2.Call import retrofit2.Callback import retrofit2.Response class SearchActivity : AppCompatActivity() { lateinit var binding: ActivitySearchBinding private lateinit var adapter: MyAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivitySearchBinding.inflate(layoutInflater) setContentView(binding.root) setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayShowTitleEnabled(false) binding.toolbar.title = "검색" val loginSharedPref = applicationContext.getSharedPreferences("login_prof", Context.MODE_PRIVATE) val userId = loginSharedPref.getString("m_id", null) val userService = (applicationContext as MyApplication).userService // 하단바 초기값 설정 val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottommenu) bottomNavigationView.selectedItemId = R.id.second_tab // 하단바 선택시 이벤티 binding.bottommenu.setOnItemSelectedListener {item -> when(item.itemId) { R.id.first_tab -> { val intent = Intent(this@SearchActivity, MainActivity::class.java) startActivity(intent) } R.id.third_tab -> { val intent = Intent(this@SearchActivity, ReviewActivity::class.java) startActivity(intent) } R.id.fourth_tab -> { if ( userId == null){ val intent = Intent(this@SearchActivity, LoginActivity::class.java) startActivity(intent) } else { val intent = Intent(this@SearchActivity, MyDining::class.java) startActivity(intent) } } R.id.fifth_tab -> { if ( userId == null){ val intent = Intent(this@SearchActivity, LoginActivity::class.java) startActivity(intent) } else { val intent = Intent(this@SearchActivity, MyProfilePage::class.java) startActivity(intent) } } } true } binding.searchBtn.setOnClickListener { var rstr_nm = binding.searchText.text.toString() val getnamecount = userService.getNamecount(rstr_nm) getnamecount.enqueue(object: Callback<Int>{ override fun onResponse(call: Call<Int>, response: Response<Int>) { if ( response.isSuccessful ){ val ncount = response.body() if (ncount != null) { if ( ncount >= 1){ val getname = userService.getName(rstr_nm) getname.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val rstrlist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, rstrlist) binding.recyclerView.addItemDecoration( DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL) ) } } override fun onFailure( call: Call<List<RstrModel>>, t: Throwable ) { call.cancel() } }) } else { Toast.makeText(this@SearchActivity, "검색 결과가 없습니다!", Toast.LENGTH_SHORT).show() } } } } override fun onFailure(call: Call<Int>, t: Throwable) { call.cancel() } }) } binding.area1.setOnClickListener { val rstr_addr = binding.area1.text.toString() val getarea = userService.getArea(rstr_addr) getarea.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.area2.setOnClickListener { val rstr_addr = binding.area2.text.toString() val getarea = userService.getArea(rstr_addr) getarea.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.area3.setOnClickListener { val rstr_addr = binding.area3.text.toString() val getarea = userService.getArea(rstr_addr) getarea.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.area4.setOnClickListener { val rstr_addr = binding.area4.text.toString() val getarea = userService.getArea(rstr_addr) getarea.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.area5.setOnClickListener { val rstr_addr = binding.area5.text.toString() val getarea = userService.getArea(rstr_addr) getarea.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.area6.setOnClickListener { val rstr_addr = binding.area6.text.toString() val getarea = userService.getArea(rstr_addr) getarea.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.area7.setOnClickListener { val rstr_addr = binding.area7.text.toString() val getarea = userService.getArea(rstr_addr) getarea.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.type1.setOnClickListener { val rstr_list = binding.type1.text.toString() val gettype = userService.getType(rstr_list) gettype.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.type2.setOnClickListener { val rstr_list = binding.type2.text.toString() val gettype = userService.getType(rstr_list) gettype.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.type3.setOnClickListener { val rstr_list = binding.type3.text.toString() val gettype = userService.getType(rstr_list) gettype.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.type4.setOnClickListener { val rstr_list = binding.type4.text.toString() val gettype = userService.getType(rstr_list) gettype.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.type5.setOnClickListener { val rstr_list = binding.type5.text.toString() val gettype = userService.getType(rstr_list) gettype.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } binding.type6.setOnClickListener { val rstr_list = binding.type6.text.toString() val gettype = userService.getType(rstr_list) gettype.enqueue(object: Callback<List<RstrModel>>{ override fun onResponse( call: Call<List<RstrModel>>, response: Response<List<RstrModel>> ) { if ( response.isSuccessful ){ val arealist = response.body() binding.recyclerView.adapter = MyAdapter(this@SearchActivity, arealist) binding.recyclerView.addItemDecoration(DividerItemDecoration(this@SearchActivity, LinearLayoutManager.VERTICAL)) } } override fun onFailure(call: Call<List<RstrModel>>, t: Throwable) { call.cancel() } }) } } }

개발자

#스프링부트

#안드롱디ㅡ

#안드로이드

#spring\

#spring

답변 1

댓글 0

보충이 필요해요 3

조회 213

2년 전 · 삭제된 사용자 님의 답변 업데이트

Flutter 코린이 질문

안녕하세요 지금 플러터로 개발에 발을 담근 코린이 인데요 다른이아니라 Stateless나 Stateful위젯 클래스를 빌드를 하면 Widget build(BuildContext context){ return … } 이 빌드 위젯이 쓰일때가 있는데요 여기서 build가 들어가는 위젯에는 왜 ()괄호 안에 String xxx 같은 변수 타입을 지정하면 빨간줄이 뜨는지 설명 해 주실 고수님들 계실까요? 그리고 해결이 된다면 저 build위젯 안에 String xxx 로 ${xxx} 를 이용하여 재사용 할 수 있는 방법도 알려주실 수 있으신지 여쭤봅니다..!

개발자

#프론트엔드

#flutter

답변 3

댓글 0

추천해요 3

조회 616