포메
[flutter (dart)] 공용 서버 컴퓨터에서 개발하기 (비추천) (adb tcpip, ssh reverse proxy) 본문
[flutter (dart)] 공용 서버 컴퓨터에서 개발하기 (비추천) (adb tcpip, ssh reverse proxy)
포포메 2025. 2. 18. 01:12나는 파이썬, 리액트 등 프로젝트를 진행할 때 대부분
항상 여러 개발자의 공용 서버 환경에서 개발해왔다.
이전 회사에서 그렇게 해왔었기 때문에, 계속 그렇게 해왔다.
공용 서버를 쓰면 이점이 있다.
각종 환경 설정을 통일하기 편하고, 데이터 공유도 쉽고 등등, 협업 개발 환경에서 공용의 서버 개발 컴퓨터를 쓰는 건 상황에 따라 괜찮다.
외부 코드 유출을 어느 정도 방지할 수 있는 보안적인 이점도 있다.
그런데 공용 서버 환경에서 앱개발을 하는 건 처음이었고, 생각보다 여러 난관이 있었다.
오늘은 그 난관 중 하나인, 안드로이드 에뮬레이터를 다뤄보고자 한다.
이전 프로젝트에서는 난관이 없었던 이유
이유는 매우 간단. "웹개발"이었기 때문. (혹은 셸 기반의 인공지능 결과 출력)
웹 브라우저는 진짜 엄청난 걸작품이다. 킹갓짱짱 크롬

내가 웹 브라우저를 걸작품이라고 생각하는데 가장 큰 이유는, "OS에 무관하게 가장 일관된 개발, 경험을 가능하게 하기 때문"이다.
크롬은 윈도우 컴퓨터, 맥 컴퓨터, 안드로이드 핸드폰, ios 핸드폰 모두에서 상당히 안정적으로 작동한다.
리액트 기반의 코드를 하나 짜두면 (반응형 디자인 정도만 해두면), 대부분의 디바이스에서 괜찮게 렌더링된다.
모든 디바이스에서 괜찮게 렌더링되게 할 수 있는 도우미 중 하나는 바로 URL이다.
웹 개발을 하면, 웹페이지에 대응되는 웹 URL을 얻을 수 있다. URL을 각 디바이스의 브라우저에 입력해서 그에 대응되는 웹사이트를 이용하는 원리이다.
앱개발에는 브라우저도 URL도 없다.
당연한 사실이지만,
앱개발을 할 때는 웹 브라우저도 웹 URL도 없다 .. !
그렇기 때문에 내가 개발한 앱을 렌더링하는 게 조금은 까다롭다. 대응되는 URL을 얻을 수도 없고, URL을 렌더링해줄 브라우저도 없다.
렌더링하려면
개발 단계에서는 에뮬레이터를 쓰고, 배포 단계에서는 빌드를 해야한다.
(리눅스 기반의) 서버 컴퓨터에서는 에뮬레이터를 실행할 수 없다. URL도 없다.
에뮬레이터 개발 시 어쩔 수 없는 점은, URL이 없다는 것.
웹개발을 서버컴퓨터에서 할 수 있었던 이유는 URL을 제공해줬기 때문이다.
사실, 서버 컴퓨터 개발 상황에서도 웹 브라우저 자체는 로컬 컴퓨터에서 실행된다.
로컬 컴퓨터의 웹 브라우저에서 렌더링할 수 있는 URL을 서버 컴퓨터가 던져주고,
그에 대응되는 웹 사이트를 서버 컴퓨터에서 실행하고 있는 것!이었다..!
이는 사실 당연한 것이다. 일반적으로 리눅스 기반의 공용 컴퓨터에서는 GUI를 구동할 수 없다.
이 사실은 웹 브라우저의 구동에도 적용되고, 앱 에뮬레이터의 구동에도 적용된다.
웹 개발에서 문제가 없었던 건, 서버 컴퓨터의 URL을 가지고, 로컬 컴퓨터의 웹 브라우저에 렌더링할 수 있었기 때문!
그렇다면 서버 컴퓨터에서 실행되는 앱 개발 코드, 그리고 로컬 컴퓨터의 앱 에뮬레이터는 ??
URL이 없기 때문에, 웹개발의 경우처럼 쉽게 렌더링하기 어렵다!
해결법
해결법은 여러가지가 있겠으나, 이번 글은 adb 명령어 기반의 tcpip 연결과 ssh reverse proxy를 결합한 방법을 설명해보겠다.
WebView를 쓰거나, flutter run -d chrome 이런 것들은 사실 약간의 단점이 있을 수 있다.
핫 리로드도 그렇고,
네이티브 관련 기능이 완전히 백프로 호환된다는 보장은 없을 것이다.
adb tcpip, ssh reverse proxy를 활용하면 로컬 컴퓨터에서 안드로이드 에뮬레이터를 구동하는 것과 동일한 개발 경험을 할 수 있다는 장점이 있다.
대략적인 코드는 다음과 같다.
AVD_LIST=$(emulator -list-avds)
FIRST_AVD=$(echo "$AVD_LIST" | head -n 1)
if [ -z "$FIRST_AVD" ]; then
echo "No AVDs found. Please create one using Android Studio or the avdmanager."
exit 1
fi
echo "Starting emulator for AVD: $FIRST_AVD"
adb emu kill
nohup emulator -avd "$FIRST_AVD" -no-snapshot-load > emulator.out &
while true; do
DEVICE=$(adb devices | grep "emulator" | awk '{print $2}')
if [ "$DEVICE" = "device" ]; then
echo "Emulator connected and ready: $DEVICE"
break
fi
sleep 1
echo "Waiting for emulator to connect..."
done
adb tcpip 5555
adb connect localhost:5555
ssh -p ${FROM_SHELL_SERVER_PORT} -R ${FROM_SHELL_FLUTTER_PORT}:localhost:5555 ${FROM_SHELL_APP_OWNER}@${FROM_SHELL_IP}
adb emu kill
간략히 원리를 설명하자면
nohup emulator -avd "$FIRST_AVD" -no-snapshot-load > emulator.out &
- 백그라운드 프로세스로 emulator 명령어를 실행하여, 자동으로 첫번째 안드로이드 에뮬레이터 실행
- 백그라운드로 하지 않으면 해당 터미널을 점유하기 때문에 이렇게 했다.
adb tcpip 5555
- 안드로이드 에뮬레이터를 tcpip 모드로 변경. 로컬 컴퓨터의 5555 포트 할당.
adb connect localhost:5555
- 안드로이드 에뮬레이터를 5555포트로 연결.
ssh -p ${FROM_SHELL_SERVER_PORT} -R ${FROM_SHELL_FLUTTER_PORT}:localhost:5555 ${FROM_SHELL_APP_OWNER}@${FROM_SHELL_IP}
- ssh로 서버 컴퓨터와 연결. 미리 할당된 ${FROM_SHELL_SERVER_PORT}로 ssh 연결을 하고, 해당 ssh 연결의 목적은 reverse proxy다. 즉, 서버 컴퓨터의 ${FROM_SHELL_FLUTTER_PORT}로 요청이 오면, 그것이 로컬 컴퓨터의 5555포트까지 전송되도록 하는 것!!
- 위에서 로컬 컴퓨터의 5555포트에 안드로이드 에뮬레이터를 연결해 놨다. 그렇기 때문에 이 reverse proxy를 통해 서버 컴퓨터의 특정 포트로 오는 요청을 로컬 에뮬레이터에서 렌더링 가능하다 !!!
단점과 비추천 이유
위 방법을 활용하면 안드로이드 에뮬레이터 기반으로는 공용 서버 컴퓨터 앱 개발이 어느 정도 가능하다.
다만 단점이 많다.
1. flutter의 경우 flutter binary 하위 폴더에 빈번한 writing 작업이 일어난다.
- ${FLUTTER_PATH}/bin 이하의 cache 폴더 등등에서 빈번한 write 작업이 자동으로 발생한다.
- 그런데 그 write 작업 중 일부는 777 권한 상태일 때도 불가하도록 설계되어 있다. 해당 파일, 폴더의 직접적 소유주일 때만 가능하다.
- 그래서 flutter binary를 서버 컴퓨터에서 공유하면 관리가 골치가 아프다. 하지만 그렇다고 binary를 공용으로 관리하지 않기 시작하면, 공용 서버 컴퓨터를 사용하는 이점이 떨어진다 ㅋㅋㅋㅋ ㅠㅠ. 딜레마 ,,, 흑
- writing 작업 관련 우회야 설계해볼 수도 있겠지만, 굳이 ??라는 생각이다. 큰 이득도 없는데 굳이 flutter 개발자의 설계를 거슬러서 그렇게까지 할 필요는 없다고 생각한다.
2. tcpip 통신이 종종 끊긴다.
3. 어차피 IOS 에뮬레이터는 위와 같은 방식의 연결을 제공하지 않는 것으로 안다. (2025.02.18 기준). 그래서 어차피 IOS 개발은 빌드를 해보든가, 더 골치가 아프다 ㅠㅠ
위와 같은 이유로 굳이 ~~~ 공용 서버 컴퓨터에서의 flutter 개발은 비추천이고,
우리 회사도 로컬 컴퓨터에서 개발하는 방식으로 재설계했다.