<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>coding etude</title>
    <link>https://codingetude.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 27 Aug 2026 08:37:36 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>코코리니</managingEditor>
    <item>
      <title>[Flutter] Socket_io_client 채팅기능을 만들어보자(1)</title>
      <link>https://codingetude.tistory.com/124</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;webSocket 과 socket.io 중 왜 socket.io를 선택 했을까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능과 편의성을 위해서 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, 이 둘의 차이점을 알아야 상황별로 어떤 것을 사용 하느냐를 정할 수있겟지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색하면 다 나오는 내용이니 간략하게 만 정리하자면.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;webSocket은 프로토콜, socket.io는 라이브러리 이다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토콜은 다들 알겠지만, 우리 이렇게 합시다~ 라고 명시된 규약이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 그대로 양방향&amp;nbsp; 실시간 소통을 위한 규약 이라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;빠르고, 적은용량을 주고 받으며, 단순 주고 받는것만 가능&lt;/span&gt;하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, socket.io는 ws(webSocket)를 활용해서 만들어진 라이브라리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 주고 받는 기능을 포함하여, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;room 과 broadcast 기능을 이용하여 하나의 채팅방에 소속된 사용자들을&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;세밀하게 관리가 가능&lt;/span&gt;하게 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 서버에서 socket.io를 사용 한다면, Flutter에서는 Socket_io_client를 사용해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;web 전용 socket.io를 flutter에서 사용 할 수 있게 dart언어로 변환한 package 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;socket (socket_io_client) 의 속성과 기능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;socket에 속한 주요 기능은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;io():&lt;/b&gt; 초기 서버의 도메인을 등록하고 socket을 생성.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;connect():&lt;/b&gt; 초기화 된 socket을 서버의 socket과 연결.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;onConnect():&lt;/b&gt; connect() 이후 연결이 완료된 후 실행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;connected&lt;/b&gt; &lt;b&gt;:&lt;/b&gt; 서버와 연결이 되어 있는지 bool 값을 return.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;leave():&lt;/b&gt; 서버에 join된 채팅방에서 연결 해제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;emit():&lt;/b&gt; 서버로 이벤트 명(name)과 데이터를 전송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;dispose():&lt;/b&gt; socket 종료.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;disconnect():&lt;/b&gt; 서버와 연결 해제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;socket의 핵심 구동 원리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Stream&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;socket을 사용하는 이유는 서버로 보낸 내용을 실시간으로 상대방에게 전송해주고, 상대방이 전송한 내용을 사용자가 실시간으로 확인하는 기능을 사용하기 위해서이다. 이 실시간 소통을 유지하려면 Flutter에서는 stream 기능을 사용해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다 알고 있겠지만,&lt;span style=&quot;background-color: #f6e199;&quot;&gt; Flutter의 stream기능은 데이터를 지속적으로 받는 기능이고 원하는 데이터를 받을 시 해당 데이터만 return 할 수 있는 기는이다. 1회성인 Future(async/await)와의 차이점&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( 그래서 스트리밍 중이라는 말은 데이터를 실시간으로 유지하고 있다는 뜻!! 깨알 상식  )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 stream은 앱 실행 시 초기에 한번만 listener를 등록해 놓으면 stream을 수동으로 cancel 하거나 앱 종료(dispose) 하지 않는 이상 계속 유지가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. room &amp;amp; broadcast&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;socket은 각각의 채팅방을 따로 controller 할 수 있다. 각 방을 room 이라고 하고, room에 속한 모든 사용자에게 내용을 전달 하는것을 broadcast라고 한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 5명이 있는 채팅에서 사용자가 메시지를 전송하면, 서버에서 비지니스 로직을 거친후 생성된 message를 roomId를 통해서 room에 속한 5명에게 동시에 전달해주는 시스템이다. 그래서 flutter에서도 동일하게 roomEntity, roomMemberEntity, messageEntity가 필요하다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;socket의 실행 순서&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;App 실행 |&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;|- initialize() + stream listener 등록&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;|- 채팅방 입장 -&amp;gt; connect() + onConnect() -&amp;gt; joinRoom()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;|- sendMessage()&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;|- 채팅방 퇴장 -&amp;gt; leave()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;|- logout -&amp;gt; disconnect()&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드 예시&lt;/b&gt;&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1769530649212&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Socket.io? _socket;
// stream controller
final StreamController&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt; _controller =
  StreamController&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt;.broadcast();
  
// initalize
void init(Sting token, String host){
  _socket = IO.io(host, IO.OptionBuilder()
    .setTransports(['websocket']) // websocket 전용
    .setAuth({'token': token})
    .disableAutoConnect() // connect() 수동 제어
    .build()
  );
  
  // stream controller 이벤트 등록
  // onAny(): 서버에서 전송된 모든 이벤트 받음.
  // on(event, data): 서버에서 전송된 특정 이벤트만 받음.
  // 모든 이벤트를 stream controller에 추가.
  _socket.onAny((event, data) {
    _controller.add({
      'event' : event,
      'data' : data
    });
  });
}

// connect
void connect() {
  _socket.connect()
}
void join (int roomId) {
  // 서버의 이벤트 네임과 동일해야함.
  _socket.emit('join_room', roomId);
}
void sendMessage(MessageEntity message){
  _socket.emit('chat_message', message);
}
void leaveRoom(int roomId){
  _socket.emit('leave room', roomId);
}
void disconnect(){
  _socket.disconnect();
}

//!! 중요 Stream 설정
// onAny()에서 이벤트 전송 받으면 _controller로 전송
// _controller 에서 eventName과 같은 이벤트라면(where) data를 return 해준다.
Stream&amp;lt;T&amp;gt; subscribeEvent&amp;lt;T&amp;gt; (String eventName, T Function(dynamic data) mapping) {
  return _controller.stream
    .where((event) {
      return event['event'] == eventName;
    }).map((event){
      return mapping(event['data]);
    })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 socket에 대한 이론적인 부분과 설정 예시코드를 정리 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 실제 flutter code에서 언제 어디서 어떤 방식으로 사용 되는지 알아보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝.&lt;/p&gt;</description>
      <category>Flutter(Dart)</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/124</guid>
      <comments>https://codingetude.tistory.com/124#entry124comment</comments>
      <pubDate>Wed, 28 Jan 2026 01:23:27 +0900</pubDate>
    </item>
    <item>
      <title>[flutter]Google_sign_in 7.1.0 이상 버전 초기화에 대해..(initialize)</title>
      <link>https://codingetude.tistory.com/123</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 구글 로그인 좀 적용하려고 패키지를 설치했는데, 웬걸?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 초기화 방식이 변경되고 사용 방법이 변경 됐어. 지금 3일 만에 해결함..ㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신버전 &lt;b&gt;Google_sign_in: ^7.2.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;성질 급한분은 설명 필요없이 가장 아래쪽 요점 정리만 봐도됨.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;증상&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;//IOS&lt;br /&gt;&lt;span style=&quot;color: #1b1c1d; text-align: start;&quot;&gt;PlatformException(google_sign_in, Your app is missing support for the following URL schemes: com.googleusercontent.apps., NSInvalidArgumentException, null)&lt;/span&gt;&lt;span style=&quot;background-color: #e9eef6; color: #1b1c1d; text-align: start;&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;// ANDROID&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;GoogleSignInException(code GoogleSignInExceptionCode.unknownError, [28404] Failed to retrieve an ID token, null)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;원인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 기존의 initialize 방식이 변경 되었음.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;signIn.initialize(clientId: clientId, serverClientId: serverClientId)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;span style=&quot;background-color: #ffc1c8; text-align: left;&quot;&gt;serverClientId 값은 필수 값으로 변경&lt;/span&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;되어서 무조건 입력해줘 하는것으로 변경 되었는데, 보통 google cloud console 에서 생성한 clientId 값을 입력해 주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;serverClientId 값은 내가 가진 서버에서 인증요청을 하면 구글에서 &lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;serverClientId&lt;span&gt; 값을 비교해서 인증을 해주는 형식으로 진행하기 때문에 필수로 변경 되었어.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span&gt;그래서, 우선 생성한 clientId 값을 수동으로 입력해서 테스트를 했는데, 해당 id는 인증할수 없다는 에러가 발생해.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span&gt;흠.. 인증 절차를 간소화 하기 위해서 firebase를 이용하기로 했어. 다들 알겠지만, firebase와 google cloud는 서로 자동으로 연동되니까 firebase에서 프로젝트 생성 후 bundle ID 값만 일치 한다면 google cloud의 정보를 자동으러 연동해주고, 또 flutter 에서 plug in을 설정해 주면 자동으로 정보를 사용 할 수 있도록 해주거든.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span&gt;그래서 firebase에서 프로젝트 생성 후 앱을 추가해서 google-servervices.json 파일과 google-services.plist 파일을 다운 받아서 각 os 폴더에 고이 넣어주고, 다시 진행 했지.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span&gt;동일한 오류로 인증 거부... 흠.. 계속 검색을 했지만 모든 자료가 7.1.0 버전 이전의 구 버전 데이터 ... 흠....&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span&gt;그래서 애초에 이런 오류는 있을 수 없다.. 뭔가 내가 놓친 부분이 있을거다. 라고 생각하고 공식문서를 정독했지..&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span&gt;그런데 똭!! 하고 한줄이 눈에 보이더라고..&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;만약 google-services.json 을 사용 중이라면, 그 안에 Web OAuth 클라이언트 (client_type = 3)가 포함되어 있어야 &lt;br /&gt;serverClientId를 코드에서 생략해도 될 수 있음.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아하... 필수로 변경 됐다고 제일 위에 써놓고.. 이걸 아래쪽에 작게 써놨구나.. 하하...;;;;;;;;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, GCP(google cloud console) 에서 바로 OAuth client ID 를 web 로 생성하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 firebase에 프로젝트로 가서 각각의 앱(IOS, ANDROID)에서 google-services 파일을 다시 받아서 각 os 폴더에 넣어줬지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 초기화를 간소화 했어.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;signIn.initialize();&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 로그인 성공....&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;내용 요약&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Google_sign_in 7.1.0 부터 초기화 및 사용 방법이 변경됨.&lt;br /&gt;2. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;초기화 시 serviceClientId 값을 필수&lt;/span&gt;로 입력해야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 하지만, &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;google-services.json / Google-services.plist 파일 내부에 client_type=3 인 clientId 값이 포함되어 있으면,&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;serviceClientId&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt; 값을 입력하지 않아도 됨&lt;/b&gt;&lt;/span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;4. GCP(google cloud console) 에서 OAuth client id 를 web으로 생성.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;5. firebase 에서 프로잭트 생성 후 동일한 bundle ID 값으로 앱 생성하고 google-services 파일을 받아서 다시 각 os 폴더에 넣어줌.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;과정요약&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;1. GCP에서 ios, android, web 용으로 각각 OAuth Client ID를 생성.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;2. firebase 프로젝트 생성, 앱 추가(ios, android)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;3. google-service.json / Google-Services.plist 파일 다운 후 각 OS 폴더에 저장&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;4. GoogleSignIn.initialize() 실행 후 정상 작동 확인.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;참고&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;1. firebase 를 사용하려면&amp;nbsp; build gradle에 &amp;nbsp;'com.google.gms.google-services' 추가해 줘야함.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;(두 곳에 추가해야함. flutter 공식문서에 나와 있지만, 모르겠거나 귀찮다면 댓글로 문의)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;2. firebase authentication 을 사용하면 이딴거 필요없다고 함. firebase가 자동으로 인증을 해줌.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;(자체 서버 구동 및 AWS 등 사용할 때 참조)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;3. 사용 방법도 변경 되었지만, 그 부분은 친절하게 공식문서에 이미지 첨부되서 깔끔하게 나와 있음.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;4. 이런거 다 귀찮으면 그냥 7.1.0 이하 버전 사용하면 됨.(7.1.0이상 부터는 뭔가 더 명확해지고 보안적으로 안전해 졌다고 하긴함.)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;끝.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Flutter(Dart)</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/123</guid>
      <comments>https://codingetude.tistory.com/123#entry123comment</comments>
      <pubDate>Wed, 19 Nov 2025 11:30:00 +0900</pubDate>
    </item>
    <item>
      <title>[flutter error] Swift Compiler Error (Xcode): No such module 'NaverThirdPartyLogin'/Users/user/Documents/projects/sowant/ios/Runner/AppDelegate.swift:3:7</title>
      <link>https://codingetude.tistory.com/122</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;네이버 로그인 사용 시 발생하는 오류..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;flutter_naver_login: ^2.2.1(현재 최신버전)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜케 최신버전에 자꾸 뭔가를 바꾸는지...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 오류가 발생하는지 보다 보니. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;2.2.0버전 부터 뭐가 바뀌었고. 마이그레이션 하려면 이렇게 해라 하고&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;이미지로 만들어져 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;해결&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;AppDelegate.swift 파일의 import 부분이 &lt;span style=&quot;color: #595959; text-align: left;&quot;&gt;import NaverThirdPartyLogin -&amp;gt; import NidThirdPartyLogin 으로 변경되었다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #595959; text-align: left;&quot;&gt;공식문서 꼼꼼히 보자....&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #595959; text-align: left;&quot;&gt;끝.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Flutter(Dart)</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/122</guid>
      <comments>https://codingetude.tistory.com/122#entry122comment</comments>
      <pubDate>Fri, 14 Nov 2025 01:07:19 +0900</pubDate>
    </item>
    <item>
      <title>[flutter IOS error] [!] CocoaPods could not find compatible versions for pod &amp;quot;firebase_core&amp;quot;:  In Podfile:</title>
      <link>https://codingetude.tistory.com/121</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;firebase_core:&amp;nbsp; ^4.2.1(현재 최신버전)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가는날이 장날이라고.. 최신 버전 이 10일 전에 배포되었고.. 설치 후 IOS에서 빌드 에러가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 프로잭트는 3.8버전을 사용 했었는데.. 딱히 문제는 없었지.. 뭐가 업데이트 된걸까...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 podfile 파일의 platform 설정을 12.0 에서 13.0으로 변경했다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 13.0으로 업데이트 하면 해결이 되는데.. 실패..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. pod를 재설치....... 실패..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Xcode 설정 변경 실패.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 폭풍검색.... 실패..(다 똑같은 말뿐...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한시간 정도 검색하다가 패키지의 배포 타겟의 요구값을 확인하는 명령어를 찾았다...ㅠㅠ&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;grep -R &quot;s.ios.deployment_target&quot; -n ios || true&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어를 실행하면 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;s.ios.deployment_target&lt;/b&gt;&lt;/span&gt; 의 값을 찾아보면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 4.2.1의&amp;nbsp;s.ios.deployment_target = 15.0 으로 되어 있다.. 하...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ffc1c8;&quot;&gt;podfile의 platform: 15.0으로 수정하고 xcode의 deployment target을 15.0으로 동일하게 수정&lt;/span&gt;해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 원래 하는 방법 대로 진행해 주면 된다,&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;cd ios&lt;br /&gt;pod repo update&lt;br /&gt;pod install --repo-update&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝.&lt;/p&gt;</description>
      <category>Flutter(Dart)</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/121</guid>
      <comments>https://codingetude.tistory.com/121#entry121comment</comments>
      <pubDate>Fri, 14 Nov 2025 00:56:49 +0900</pubDate>
    </item>
    <item>
      <title>[홈서버 구축] SK 브로드밴드 홈서버 외부 접속 설정 하기</title>
      <link>https://codingetude.tistory.com/120</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;결론 부터 말하자면&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; SK브로드밴드&lt;/span&gt;&lt;/b&gt;를 사용 하면 홈서버를 &lt;b&gt;무료로 구축할 수는 없다..&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 오늘은 외부에서 서버에 접속 할 수 있도록 하는 방법에 대해서만 이야기 해보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 시작은 통신사(ISP)의 공유기 단말을 통해서 서버컴에 접근해야 하기 때문에 공유기 설정방법 부터..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sk의 공유기 설정 페이지 접속 관련해서는 검색하면 바로 나오니까 패스.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유기 설정페이지로 접속 했다면, 우선 DHCP 설정을 진행 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 기기별 고유 mac 주소가 있는듯 한데. 이것을 등록해서 이 pc( 혹은 랩탑 )에 고정 ip를 할당 하겠다는 예약을 걸어 놓는것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;nbsp;mac 주소 확인&lt;br /&gt;win - shell or cmd : ipconfig /all (물리적 주소가 mac 주소)&lt;br /&gt;ex) a3-t3-e4-t5-00-00 으로 되어 있지만 입력 할는 - 대시 : 로 변경해주어야 한다.&lt;br /&gt;ex) a3:t3:e4:t5:00:00&lt;br /&gt;&lt;br /&gt;Linux - terminal : ip addr (link/ether 값을 찾으면 됨)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설정해 놓으면 동일 네트워크에서 설정해 놓은 ip로 접속시 서버 컴으로 접속할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 외부에서 접속하게 하려고 한다면 포트 포워딩/DDNS 를 설정해 중야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;포트 포워딩 이란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버컴에 접속하기 위해서 &lt;b&gt;특정 포트를 설정하여 설정한 포트로만 접속 할 수 있도록&lt;/b&gt; 하는것다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것 또한 접속 ip에&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt; DHCP에서 설정한 ip를 넣어주고 포트는 원하는 포트 번호로 설정&lt;/b&gt;&lt;/span&gt;해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, https 로 접속하기 위해서는 443 포트로 고정해야 하고 443 포트는 ssl 인증서를 받아야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DDNS( &lt;span style=&quot;background-color: #ffffff; color: #001d35; text-align: start;&quot;&gt;Dynamic DNS&lt;/span&gt; ) 란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 가정에서 사용하는 인터넷은 공급업체(ISP)에서 유동 ip를 제공해서 ip주소가 지속적으로 변경 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;domain 주소를 생성 후 도메인 주소로 접속하면 해당 ip로 접속&lt;/b&gt;&lt;/span&gt;하게 해주는 설정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 필요한 것은 domain 주소이다. 무료와 유료가 있고 각 사이트의 정책별로 갱신주기 및 가격이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 어차피 서버 주소기 때문에 무료 도메인 생성하려고 &lt;b&gt;freenom&lt;/b&gt;에 접속해서 30번 정도 검색했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 무료 도메인은 전혀 검색이 되지 않았다. 그리고 &lt;b&gt;freenom&lt;/b&gt;은 엄청 느리기 때문에 인고의 시간을 보내다,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 포기 하고 다른 사이트를 찾았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째는 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;no-ip&lt;/b&gt;&lt;/span&gt; 사이트. 도메인 생성은 무료이고 30일 마다 사용 연장을 해야한다. 생성 방법도 무지 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌우당간.. noip에서 도메인 생성 후 DDNS 설정도 마무리 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 설정 후 생성한 도메인으로 접속 하면 다음 에러가 발생한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(동일네트워크 / 외부 내트워크) : CONNECTION_REFUSED / CONNECTION_TIMED_OUT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일 네트워크에서는 접속 거부, 외부에서는 시간초과. 외부에서는 접속 시도 하면 서버 근처에도 못갔다고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(일단 집에서 테스트 했기 때문에&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt; 외부 테스트는 휴대폰에 termux를 설치&lt;/b&gt;&lt;/span&gt;해서 사용했다. 휴대폰에서 터미널을 사용 하게 해주는 앱이다.. 너무 유용함...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;원인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 세팅을 다하고 sk 측에 전화도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sk 기술 상담 실장과 통화 내용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;전혀 막아 놓은것도 없고, 고객님이 설정을 잘 못한거 같은데, &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전화상으로는 알 수 없기에 기사가 방문해야 알 수 있습니다.&quot; &lt;/b&gt;&lt;b&gt;(거짓말 or 무능)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하.. 내가 뭔가 잘못했나 싶은순간..!!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어???? wan ip 가 왜 실제 접속 주소랑 다르지...??????&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 무슨 말이냐면... no ip에서 domain을 생성하면 생성당시 사용한 네트워크 ip가 자동으로 등록인 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 자동으로 등록되는 ip 주소가 공유기 설정 페이지의 wan ip 주소와 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 설명해서. 지금 내 주소는 111.111.11.1 로 되어 있는데, 실제 주소는 222.222.22.2로 되어 있는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sk는 이중 NAT를 사용 하는것이였던 것이 였다...이건 바로 내 컴터로 접속 할 수 있는 ip가 아니라&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;222.222.22.2로 접속하면 sk가 다시 우리집 ip 111.111.11.1로 연결해 준다고 보면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 DDNS, 포트 포워딩, DHCP가 모두 싸그리 다 필요없는 뻘짓이였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DDNS의 도메인으로 접속하면 우리집으로 오는게 아니라 sk 쪽으로 요청이 가는데 sk 가 이거 모두 차단하고 있는 것이 였다...&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;터미널 : $ curl ifconfig.me&amp;nbsp;&lt;br /&gt;입력하면 실제 사용되는 ip 주소 확인가능&lt;br /&gt;이 ip 주소가 공유기 설정 페이지의 wan ip와 같아야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;해결 방안&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. sk에 고정 ip 사용하게 해주세요 하고, 달에 추가요금을 내면 된다.. 500mb 기준 5만원 정도?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. cloudflare tunnel을 사용 해서 다이렉트로 홈서버로 꽂아 버리기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 2번의 경우 루트 도메인이 필요하기 때문에 유료 도메인이 필수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 사이트 검색결과 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;namecheap 이라는 도메인 사이트가 가장 저렴&lt;/b&gt;&lt;/span&gt;했다. 가장 저렴한게 1년 2달러/ 연장 시 15달러 정도?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어차피 1년정도 서비스 해보고 안되면 접어야 하고, 만에 하나의 경우 잘된다면 홈서버에서 외부서버로 변경하면 되기 때문에, 2달러 바로 결제하고 tunnel을 연결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 또.. 2주가 흘렀다...하..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p.s&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;참고로 cloudflare에 루투 도메인을 등록 할 때, example.com 식으로 .이 한번만 들어가야한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;example.abc.kr 이런 식으로 .이 두번 들어가면 등록 자체가 안되다. 이유는 .이 두번 들어가는건 특정 메인 도메인을 빌린 서브 도메인이기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SK 망해라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>others TIL</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/120</guid>
      <comments>https://codingetude.tistory.com/120#entry120comment</comments>
      <pubDate>Mon, 25 Aug 2025 15:34:59 +0900</pubDate>
    </item>
    <item>
      <title>[flutter android] Unresolved reference 'embedding'.</title>
      <link>https://codingetude.tistory.com/119</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;상황&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- Android Studio를 업데이트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- gradle, AGP를 업데이트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- gradle migrate를 진행&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;증상&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unresolved reference 'embedding'.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Unresolved reference 'FlutterActivity'.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Gradle task assembleDebug failed with exit code 1&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- android/app/src/main/kotlin 의 Androidmanifast.xml 파일이 인식 되지 않는 증상.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;원인 분석&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;1. kotlin , AGP, gradle 의 호환성 문제.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예전에는 버전의 호환성 문제로 빌드오류가 발생했을 때 증상과 동일한 에러가 발생한것 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만, 테스트 결과 버전에 문제가 있다면 어떤 플러그인의 버전이 문제가 있다고 정확한 에러가 발생한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;버전문제는 아님.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. IDE(android studio) 자체적 오류.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IDE의 일시적인 오류로 인해서 android 프로젝트를 인식 못하는 경우 발생한다고 하며,&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이것 역시 예전에는 새로운 창으로 android 프로젝트를 열거나 sync gradle 등의 기능을 이용해서&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오류를 해결했다고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만, 현재 IDE 버전이 업데이트 되면서 gradle의 sync는 IDE 시작시 자동으로 진행하도록 변경되서 임의적인 sync 를 진행 할 수 없는듯 하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;추가 적으로 IDE의 cache를 모두 삭제하고, IDE 삭제 후 재설치 까지 진행 했지만, 해결 안됨.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IDE 문제는 아님.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. pagkage 문제&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;간혹 서비스 중단되거나 flutter의 버전과 호환 되지 않는 package의 경우 빌드 시 발생 할 수 있다고 하고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;몇몇 유져의 경우 package 삭제 시 정상 작동 한다는 내용도 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 현재 사용 중이 package를 모두 최신 버전으로 업그레이드 진행하고, 사용하지 않는 package는 모두 제거.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 동일한 증상이 발생했고, 어떤 Package에서 발생하는지 알 수 없는 상태에서 하나씩 삭제하고 테스트 하기에서는&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일이 너무 커지기 때문에 package의 문제는 가장 마지막에 진행 해보기로 함.(하나씩 삭제하고 빌드하기.. 오류를 찾기위해서 집을 부셔야 하는 경우가 발생.....ㅎㄱㄱ)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. migrate&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;flutter 3.16 버전 이전에는 flutter를 android로 빌드하기위해 사용했던 gredle를 설정하기 위해, build.gradle에 classPath로 선언 하여 사용했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이 방식이 많은 오류를 발생기켜서 3.16 이후로 migrate를 권장하고 있다. (빌드 시 경고가 뜬다.)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존의 build.gradle에 있던 플러그인 등록을 gradle.setting로 이동 해주어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1755832288856&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Deprecated imperative apply of Flutter's Gradle plugins&quot; data-og-description=&quot;How to migrate your Flutter app's Android Gradle build files to the new, declarative format.&quot; data-og-host=&quot;docs.flutter.dev&quot; data-og-source-url=&quot;https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply&quot; data-og-url=&quot;https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/q8oZW/hyZyfxlVly/r87oLlABbHnCf16XMjrDs1/img.png?width=320&amp;amp;height=320&amp;amp;face=0_0_320_320&quot;&gt;&lt;a href=&quot;https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/q8oZW/hyZyfxlVly/r87oLlABbHnCf16XMjrDs1/img.png?width=320&amp;amp;height=320&amp;amp;face=0_0_320_320');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Deprecated imperative apply of Flutter's Gradle plugins&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How to migrate your Flutter app's Android Gradle build files to the new, declarative format.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.flutter.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 홈페이지의 예시를 보면 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 내 소스코드와 바교 했을때,&amp;nbsp; &lt;span style=&quot;background-color: #f6f7f8; color: #191c22; text-align: start;&quot;&gt;id &lt;/span&gt;&lt;span style=&quot;background-color: #f6f7f8; color: #0c7064; text-align: start;&quot;&gt;&quot;dev.flutter.flutter-plugin-loader&quot;&lt;/span&gt;&lt;span style=&quot;background-color: #f6f7f8; color: #191c22; text-align: start;&quot;&gt; version &lt;/span&gt;&lt;span style=&quot;background-color: #f6f7f8; color: #0c7064; text-align: start;&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;br /&gt;이 소스 한중이 빠져 있던걸 발견....ㅎㄷㄷ;;;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 추가 하자마자 해결...ㅠㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 loader는 gradle을 android에서 인식할 수 있게 컴파일 해주는 역할 인데..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;manifast.xml에서 flutter의 컴파일 내용을 참조해야 하는데, 그 내용이 없으니 당연히 빌드가 안되는 것...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 여러 다양한 문제에서 동일한 에러가 발생 했지만, 현재는 각 오류별로 log가 구별되서 나온다는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 소스코드를 더 자세히 살펴 보자는것... migrate 실수 한번으로 10일이 날아가 버렸다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝...&lt;/p&gt;</description>
      <category>Flutter(Dart)</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/119</guid>
      <comments>https://codingetude.tistory.com/119#entry119comment</comments>
      <pubDate>Fri, 22 Aug 2025 12:18:16 +0900</pubDate>
    </item>
    <item>
      <title>[flutter android] flutter gradle plugin migrate 하기</title>
      <link>https://codingetude.tistory.com/118</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. migrate의 이유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 flutter 버전이 업데이트 되면서 기존 세팅의 보안을 위해 migrate를 권장하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 plugin 을 android/build.gradle 에서 선언하고 android/app/build.gradle에서 사용하는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;홈페이지에 따르면 android/build.gradle에서 선언하는 방식은 많은 오류를 발생 시켰다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 3.16버전 이후 부터 android/build.gradle에서 선언하던 plugin을 build.setting에서 선언 하는 방식으로 migrate 하는것을 권장하고 있는것이다.&lt;br /&gt;다음 3가지를 순차적으로 진행 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. android/build.gradle&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은 생각보다 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 &lt;b&gt;android/build.gradle&lt;/b&gt;에서 선언 하던 내용을 &lt;b&gt;build.setting로 이동&lt;/b&gt; 후 &lt;b&gt;android/app/build.gradle&lt;/b&gt;에서 사용 하는 방식으로 변경해 주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1755771375044&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// android/build.gradle
// 삭제 이동 진행
//buildscript {
//    ext.kotlin_version = '2.0.21'
//    repositories {
//        google()
//        mavenCentral()
//    }
//
//    dependencies {
//        classpath &quot;com.android.tools.build:gradle:8.7.3&quot;
//        classpath &quot;org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version&quot;
//    }
//}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = &quot;${rootProject.buildDir}/${project.name}&quot;
}
subprojects {
    project.evaluationDependsOn(':app')
}

tasks.register(&quot;clean&quot;, Delete) {
    delete rootProject.buildDir
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 기존의 레거시 코드이다. 이 코드에서 &lt;b&gt;buildScript 부분을 삭제하고 내용은 build.setting 로 이동&lt;/b&gt; 시켜주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. android/build.setting&lt;/h4&gt;
&lt;pre id=&quot;code_1755771540193&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// android/build.setting
pluginManagement {
    def flutterSdkPath = {
        def properties = new Properties()
        file(&quot;local.properties&quot;).withInputStream { properties.load(it) }
        def flutterSdkPath = properties.getProperty(&quot;flutter.sdk&quot;)
        assert flutterSdkPath != null, &quot;flutter.sdk not set in local.properties&quot;
        return flutterSdkPath
    }
    settings.ext.flutterSdkPath = flutterSdkPath()

    includeBuild(&quot;${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle&quot;)

    plugins {
//        id &quot;dev.flutter.flutter-gradle-plugin&quot; version &quot;1.0.0&quot; apply false
        id &quot;dev.flutter.flutter-plugin-loader&quot; version &quot;1.0.0&quot;
        id &quot;com.android.application&quot; version &quot;8.7.3&quot; apply false
        id &quot;org.jetbrains.kotlin.android&quot; version &quot;1.9.20&quot; apply false
    }
}

include &quot;:app&quot;

apply from: &quot;${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;android/build.gradle의 내용을 위 코드의 plugins의 내용 처럼 선언해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분은 위에 추가된 세가지는 필수적으로 선언 되어야만 android앱이 빌드가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 부터 설명 살펴보자면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;첫번째 plugin은 flutter에서 선언한 내용을 android로 변환해주는 기본 plugin 이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;예전에는 flutter-gradle-plugin을 사용 했지만, 지금은 flutter-plugin-loader을 사용한다고 한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;두번째는 AGP(android gradle plugin)이라고 하고 gradle을 android에서 사용 할 수 있도록 도와주는 plugin이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;마지막은 보는 그대로 kotlin의 버전을 정의 하는것이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;이 3가지는 무조건 필수로 선언해주어야 빌드가 가능하다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 외부 plugin을 선언해서 사용하면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예) google-server, firebase&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. android/app/build.gradle&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 진행 헀다면, 이제 android/app/build.gradle에서 선언한 내용을 가져다 사용 하면된다.&lt;/p&gt;
&lt;pre id=&quot;code_1755772559596&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// android/app/build.gradle
plugins {
    id &quot;com.android.application&quot;
    id &quot;kotlin-android&quot;
    id &quot;dev.flutter.flutter-plugin-loader&quot;
}


// dependency 사용
dependencies {
    implementation 'com.google.firebase:firebase-analytics'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 만약 firebase와 같은 외부 plugin을 사용 할 때 그 세부적인 dependency를 아래쪽 dependencies에 선언 해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 문제 없이 빌드가 되고 기존 빌드시 나오던 deprecated 경고도 사라질 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝.&lt;/p&gt;</description>
      <category>Flutter(Dart)</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/118</guid>
      <comments>https://codingetude.tistory.com/118#entry118comment</comments>
      <pubDate>Thu, 21 Aug 2025 19:46:23 +0900</pubDate>
    </item>
    <item>
      <title>[Architecture] Clean Architecture의 이해(2)</title>
      <link>https://codingetude.tistory.com/117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 Clean Architecture의 이해(1)의 내용을 바탕으로 예제를 보면서 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 의존성의 흐름순으로 예제를 작성하는게 좋지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 조금 쉽게 이해를 돕기위해 앱의 로직순으로 예제를 만들어 보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(모든 예제는 flutter / dart 을 사용 합니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Presentation(UI)&lt;/h4&gt;
&lt;pre id=&quot;code_1753928418426&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch&amp;lt;CounterProvider&amp;gt;().counter;

    return Scaffold(
      appBar: AppBar(title: Text('Clean Architecture Counter')),
      body: Center(child: Text('Count: $counter')),
      floatingActionButton: FloatingActionButton(
        onPressed: () =&amp;gt; context.read&amp;lt;CounterProvider&amp;gt;().increment(), // provider에 이벤트 실행
        child: Icon(Icons.add),
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;provider&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753928603802&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CounterProvider extends ChangeNotifier {
  final IncrementCounter incrementCounter;

  Counter _counter = Counter(0);
  Counter get counter =&amp;gt; _counter;

  CounterProvider({required this.incrementCounter}); // 의존성 주입(DI)(constructor)

  Future&amp;lt;void&amp;gt; increment() async {
    _counter = await incrementCounter(_counter); // 주입된 의존성 사용
    notifyListeners(); // 값이 변경됨을 알림
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Domain&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;use case&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753928870994&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class IncrementCounter { // Use case
  final CounterRepository repository;

  IncrementCounter(this.repository); // 의존성 주입(DI)(constructor)

  Future&amp;lt;Counter&amp;gt; call(Counter counter) {// entity를 사용해 타입선언(검증)
    return repository.increment(counter); // repository로 data영역에 이벤트 요청
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;domain pository&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753928945754&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abstract class CounterRepository {
  Future&amp;lt;Counter&amp;gt; increment(Counter counter);
}

// 단순 interface 역할만을 담당&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;entity&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753929105539&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Counter {
  int value; // 단순하게 데이터의 검증 역할

  Counter(this.value);
}

// 왜 검증인가? 
// type 언어에서 Type 선언 했을 때, type 이 맞지 않으면 type 에러를 유발하기 때문에
// type 선언 만으로도 data의 검증 역할을 할 수 있음.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Data&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;repository&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753929562170&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CounterRepositoryImpl implements CounterRepository { // domain의 repo를 실제로 구현
DataSource remote;
CounterRepositoryImpl(this.remote); // 의존성 주입
  @override
  Future&amp;lt;Counter&amp;gt; increment(Counter counter) async {
    return await remote.increment(counter); // data source 호출
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;data source&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753930108547&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CounterDataSourceImpl implements CounterDataSource {
  final NetDriver netDriver;
  ApplicationDataSourceImpl(this.netDriver); // newwork 의존성 주입
  
  @override
  Future&amp;lt;Counter&amp;gt; increment(Counter counter) async {
    String json = CountModel.toJson(counter); // model을 통해 json으로 변환.
    String url = 'api 주소';
    
    final respone = await netDriver.increment(url, json); // netDriver의 http 통신 
    return respone.fromJson(); // 서버에서 받은 데이터를 model을 통해 다시 int로 변환
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;model&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753930643099&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CounterModel {
  int value;

  CounterModel(this.value);
  
  factory CounterModel.fromJson(Map&amp;lt;string, dynamic&amp;gt;) {
  	return CountModel(value: (json['counter'] as num?)?.toInt())
  } // json을 CounterModel로 변환
  
  Map&amp;lt;String, dynamic&amp;gt; toJson(this) {
  	return {'value': this.value};
  }// CounterModel을 json으로 변환.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 clean architecture의 간단한 로직을 예제로 만들어 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 간단한 예제 이지만, 이 예제 속에서 데이터의 흐름과 의존성이 어떤식으로 주입되는지를 찾을 수 있다면 큰 틀을 이해 안것이라고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 세부적으로 알아야 할 것은 use case이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;use case는 단순 거처가는 통로가 아니라 데이터를 가공하는 역할&lt;/b&gt;&lt;/span&gt;을 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;데이터를 가공하거나 새로 만들어야 한다면 use case 내에서&lt;/b&gt;&lt;/span&gt; 이루어 져야하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외 영역에서(UI, Data)에서는 데이터를 가공 하면 안된다는 점을 유의 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 불가능하게 막아 놓은것은 아니지만, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;다른 영역에서의 작업은 코드의 유지보수에 어려움을 야기하고 clean architecture 사용 장점을 퇴색 시키기 때문에 철저하게 지키는게 좋다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅은 여기서 마무리 하고 다음 포스팅은 DI(dependency injection) 의존성 주입에 대해서 간략하게 포스팅 해볼까 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝.&lt;/p&gt;</description>
      <category>Flutter(Dart)</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/117</guid>
      <comments>https://codingetude.tistory.com/117#entry117comment</comments>
      <pubDate>Thu, 31 Jul 2025 12:11:30 +0900</pubDate>
    </item>
    <item>
      <title>[Architecture] Clean Architecture 의 이해(1)</title>
      <link>https://codingetude.tistory.com/116</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 클린 아키텍쳐에 대해서 한번 정리해 보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Clean Architecture란 무엇인가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Clean architecture는 앱 전체의 구조 설계를 정의하는 용어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해서 내가 개발하는 프로덕트의 폴더구조를 뜻하는 것으로 폴더를 계층화 시키는 것으로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 그대로 '깨끗한 구조' 를 말한다. 왜 깨끗한, 깔끔한 구조라고 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금부터 자세히 알아보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Clean architecture는 왜 사용 하는가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Clean architecture의 기본 개념이 위에서 설명한 계층을 분리하는것에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 계층을 분리해서 사용하는 것의 목적은 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Clean architecture는 기분 적으로 의존성(DI)을 외부에서 주입 받아서 사용 하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은, 계층별로 상위와 하위가 분리되고 의존성은 상위에서 하위로 흐르게 된다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 하위계층은 상위 계층에서 어떤일이 일어나는지 전혀 알 수 없고, 주어진 명령만 실행하면 되는 구조가 되는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 mockup을 주입해서 &lt;span style=&quot;background-color: #ffc9af;&quot;&gt;테스트에 용이하고&lt;/span&gt;, 주어진 명령만 수행 하면 되기 때문에 외부영역과 철저하게 단절되어 &lt;span style=&quot;background-color: #ffc9af;&quot;&gt;유지보수가 쉬워진다&lt;/span&gt;. 이것이 &lt;span style=&quot;background-color: #ffc9af;&quot;&gt;Clean architecture를 사용하는 궁극적인 목표&lt;/span&gt;이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;계층의 구조와 역할&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 개념과 목표를 글로 표현하자면 너무 어렵게 느껴질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 각 계층이 어떻게 분리되고 어떤 역할을 하는지 다음 구조를 확인해 보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;/project-root &lt;br /&gt;── /domain &lt;br /&gt;│├── /entities&lt;br /&gt;│├── /repositories(inteface)&lt;br /&gt;│└── /usecases &lt;br /&gt;│ &lt;br /&gt;├── /data &lt;br /&gt;│├── /models &lt;br /&gt;│├── /repositories &lt;br /&gt;│└── /datasources &lt;br /&gt;│ &lt;br /&gt;├── /presentation &lt;br /&gt;│├── /view &lt;br /&gt;│├── /viewmodel (또는 bloc, controller, provider 등의 상태관리) &lt;br /&gt;│└── /widgets(components)&lt;br /&gt;│ &lt;br /&gt;├── /infrastructure &lt;br /&gt;│├── /network &lt;br /&gt;│├── /database &lt;br /&gt;│└── /external &lt;br /&gt;│ &lt;br /&gt;├── /di (dependency injection) &lt;br /&gt;│ &lt;br /&gt;└── main(앱의 진입점)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 폴더 구조와 같이 진입점으로 시작해 presentation, domain, data 영역으로 계층이 분리가 된다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 49.5349%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.4419%; text-align: center;&quot;&gt;&lt;b&gt;계층&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 32.093%; text-align: center;&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.4419%; text-align: center;&quot;&gt;data&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;서버와 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.4419%; text-align: center;&quot;&gt;domain&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;각 domain의 명령 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.4419%; text-align: center;&quot;&gt;presentation&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;UI 영역&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;데이터의 흐름은 &lt;b&gt;data &amp;gt; domain &amp;gt; presentation&lt;/b&gt;으로 흐르고&lt;b&gt; 의존 주입 역시 동일한 순서&lt;/b&gt;로 흐르게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 알 수 있듯 &lt;b&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;data가 가장 상위, presentation이 가장 내부의 계층&lt;/span&gt;&lt;/b&gt;이 되는것다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 계층에서는 주어진 역할만 수행하게 되기 때문에 유지보수 할 때 수정 사항의 영역만 핸들링 해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 각 계층에서 어떤 방식으로 의존성을 주입 받고 계층별로 어떤 방식으로 소통되고 진행 되는지 알아보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;presentation(UI)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 영역은 view의 역할을 한다. 말 그대로 user가 직접적으로 사용하는 영역이다. view 영역에서 사용자로 인해 이벤트가 발생하면 view model로 이벤트를 전달한다. 그럼 view model은 주입되어 있는 의존(domain)으로 다시 이벤트를 전달한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;doamin&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;domain영역은 각 domain에서 요청하는 데이터를 수집, 가공해서 UI에 전달하는 역할을 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;domain에 속하는 각 영역별 기능을 알아보자.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 88.2558%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.4319%; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 103.851%; text-align: center;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.4319%; text-align: center;&quot;&gt;use case&lt;/td&gt;
&lt;td style=&quot;width: 103.851%;&quot;&gt;data 영역에 필요한 data를 요청하여 presentation에서 요청한 data로 가공하여 보내준다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.4319%; text-align: center;&quot;&gt;entity&lt;/td&gt;
&lt;td style=&quot;width: 103.851%;&quot;&gt;presentation에서 사용되는 data의 model(요청한 데이터를 검증 및 확인에 쓰임)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.4319%; text-align: center;&quot;&gt;repository&lt;/td&gt;
&lt;td style=&quot;width: 103.851%;&quot;&gt;abstract로 data영역과 Brigde 역할을 담당.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;presentation 영역에서 실제로 이벤트를 받아오는 역할은 use case에서 담당한다.&amp;nbsp; 이 use case 영역은 데이터를 요청하고 받아온 데이터를 가공해서 view model에 전달하는 역할을 한다. 이 과정을 조금 더 상세하게 풀어본다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;use case&lt;/b&gt;에서 &lt;b&gt;repository&lt;/b&gt;를 통해 &lt;b&gt;데이터를 받고&lt;/b&gt;, 그 데이터를 &lt;b&gt;entity&lt;/b&gt;로 &lt;b&gt;검증&lt;/b&gt;하고, 가공해서 &lt;b&gt;view model로 반환&lt;/b&gt;&lt;/span&gt;시켜준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 것이 도메인의 역할 이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;data&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 서버와 소통하는 영역이다. 서버에 데이터를 요청하고 요청 결과에 따라서 domain으로 결과를 전달하는 역할을 한다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 88.0216%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 76.8094%;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;model&lt;/td&gt;
&lt;td style=&quot;width: 76.8094%;&quot;&gt;데이터를 변환하는데 사용.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;repository&lt;/td&gt;
&lt;td style=&quot;width: 76.8094%;&quot;&gt;domain의 abstract의 repository를 상속하여 실제 구현 하는 영역.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;data source&lt;/td&gt;
&lt;td style=&quot;width: 76.8094%;&quot;&gt;infrastructure/network를 연동하여 서버와 소통하는 역할.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;domain에서 요청된 데이터를 &lt;b&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;repository가 data source로 전달&lt;/span&gt;&lt;/b&gt;하면, &lt;b&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;data source에서&lt;/span&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;model을 활용하여 변환하고&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt; network로 서버에 요청&lt;/span&gt;&lt;/b&gt;하고, &lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;반환된 값을 다시 model을 활용하여 변환 후 repository를 통해 domain으로 전달&lt;/b&gt;&lt;/span&gt; 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;요약 정리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념과 로직을 말로 전달 하기에는 너무 어렵고 무리가 있다고 생각한다. 다음 포스팅은 이 내용을 바탕으로 구현된 간단한 로직의 예시들을 보면서 설명할 예정이다. 그럼 이번 포스팅을 정리하면서 마무리 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. clean architecture는 분리 계층 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 각 계층은 외부 계층에 대해서 철저하게 분리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 의조성의 흐름과 데이터의 흐름은 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Data &amp;gt; Domain &amp;gt; Presentation)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 계층의 분리와 의존성의 주입으로 테스트가 쉽고, 유지보수가 간결하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅은 예제를 보면서 이론을 숙지해보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝.&lt;/p&gt;</description>
      <category>Flutter(Dart)</category>
      <category>Clean Architecture</category>
      <category>클린 아키텍쳐</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/116</guid>
      <comments>https://codingetude.tistory.com/116#entry116comment</comments>
      <pubDate>Thu, 31 Jul 2025 11:07:41 +0900</pubDate>
    </item>
    <item>
      <title>[flutter error] Error connecting to the service protocol:</title>
      <link>https://codingetude.tistory.com/115</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 생성 후 처음 시뮬레이터로 디버깅 모드 실생 했을 때 처음 보는 에러가 발생 했다...뭐지..?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;발생 원인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 해보니 이유는 특정되지 않지만 원인은 빌드과정에서 문제가 발생하는듯 하다.(너무 뻔한가..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 시뮬레이터와 연결되기전에 앱이 너무 빨리 종료되서 발생.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 기존 작업하던 다른 앱의 cache가 남아 있을 때 발생.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. android 시뮬레이터일 경우 Quick boot를 사용하면 간혹 발생.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 에러에 나오는 port 에 이미 다른 작업이 진행 되고 있을 때 발생.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. IDE 또는 pc(or 노트북) 의 vm 에 일시적 오류일 떄 발생.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. flutter 의 package가 오류를 낼때 발생.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 해결 방법을 살펴 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방법 1 : 기존 기록 삭제 하기 / 기존 cache 제거 하기&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;flutter clean&lt;br /&gt;flutter pub get&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 안된다면 캐쉬도 삭제해 보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Android studio &amp;gt; File &amp;gt; Invalidate Caches 실행.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방법 2 : android 시뮬레이터 새로 생성해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 android 시뮬레이터에서 발생 한다면, 시뮬레이터 삭제 후 새로 생성 할 때 boot 항목을 cool boot로 설정해 주고 생성.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방법 3 : port정보를 확인 하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 되는 port정보를 확인 한 후 사용중인 내용 멈추기&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;터미널 : lsof -i :포트번호&amp;nbsp;&lt;br /&gt;* 정보가 안나오면 port 문제는 아님.&lt;br /&gt;* 정보가 나온다면 해당 port를 죽이고 다시 실행해 봐야함.&lt;br /&gt;* port 정보의 pid 번호 확인.&lt;br /&gt;터미널 : kill -9 PID&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방법 4 : package 오류 확인해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 쉬운 재부팅. 해결되면 다행이지만 안된다면 마지막으로 빌드과정에서 package에 오류 일 가능성이 가장 높음.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;flutter run -v&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 빌드과정의 log를 확인 할 수 있다. 어느 package에서 오류가 나오는지 확인이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 너무 많은 내용이 지나가고 사람인지라 log내용을 못보거나 분석 못하거나 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인의 경우 log에서 google-moblie-ads가 빌드되는 과정에서 뭔가 오류가 발생하고 넘어가는 로그를 찾았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 사용할 거라 생각없이 추가 했었는데, 패키지가 빌드되면서 그 설정값이 없어서 발생하는 오류 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 패키지를 삭제했더니 다행히 정상 작동 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 오류가 갑자기 발생한다면 패키지의 오류일 가능성이 가장 크다고 생각이 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 설치한 패키지가 최신 버전인지, 설치하고 설정을 정확히 해줬는지, 아니면 현재 deprecated 상태가 아닌지, 비슷한 패키지와 충돌이 나고 있는건 아닌지 여러방면으로 확인해 봐야 할것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝.&lt;/p&gt;</description>
      <category>Flutter(Dart)</category>
      <author>코코리니</author>
      <guid isPermaLink="true">https://codingetude.tistory.com/115</guid>
      <comments>https://codingetude.tistory.com/115#entry115comment</comments>
      <pubDate>Wed, 30 Jul 2025 19:50:55 +0900</pubDate>
    </item>
  </channel>
</rss>