이 글은 삼성전자와 하이닉스의 약 20여년 가까운 역사와 기타 풍문등 잡다한 이야기에 관한 것입니다.

나름 필자는 전자공학을 전공했고, 두 회사 중 한 회사에서 일해보기도 했고, 그래서, 주변 지인들이 이 두 회사에 다녔거나 다니고 있기도 하고, 전후에 많은 이야기를 듣기도 했습니다.

 

반도체 회사? 근데 둘이 전혀 다른 회사예요?

삼성전자와 SK하이닉스, 둘 다 반도체 회사라고 부르지만 사실 성격이 꽤 다릅니다.
삼성전자의 사업부를 보면 반도체(DS), 가전·모바일(DX), 디스플레이(SDC) 등으로 구성돼 있어요. 반도체도 만들고, TV·스마트폰 같은 완성품도 만드는 거죠.
반면 SK하이닉스는 반도체 메모리, 즉 DRAM과 NAND 플래시만 만듭니다. 완성품은 없어요.
자동차에 비유하자면, 삼성전자는 자동차도 만들고 엔진도 만드는 회사, SK하이닉스는 엔진만 만드는 회사라고 보시면 딱 맞습니다.

 

 

 

 

삼성전자의 간단한 역사

지금은 반도체 하면 삼성이지만, 사실 삼성전자의 출발은 TV 제조사였어요. 1969년 창업해서 1970년대에 흑백 TV로 큰 성공을 거뒀고, 이후에도 TV 분야에서 꽤 오랫동안 세계 시장 점유율을 탄탄하게 유지했습니다.


반도체의 시작은 1974년으로 거슬러 올라갑니다. 한국반도체를 이병철 회장이 인수하면서 반도체 사업에 첫 발을 내딛었어요.

이후 1983년에 본격적인 반도체 투자 선언("도쿄 선언")을 하고, 1980~90년대 내내 "세계 최초" 타이틀을 달고 메모리 반도체를 개발해 나갑니다. (아래 그림 참고)

 

출처: 나무 위키

 

한번은 하이닉스 다니던 선배님이 그런 얘기를 하신 적이 있습니다. 이야기를 들은지는 한 10년도 더 된 것 같은데, "기술로 따졌을 때, 하이닉스가 삼성전자에 그렇게 밀리지 않는다"고요. 다만, 삼성전자가 세계 최초 타이틀을 내세워서 뭔가 개발하기 시작했을 무렵부터, 하이닉스(구. 현대반도체, LG반도체) 가 딱 2~3개월 정도 밀리기 시작했는데, 그게 이 당시에 한창 유행하던 파업기간 때문이라고 얘기했습니다. - 한국에서 1980년대 말 즈음에는 임금인상을 이유로 파업을 정말 많이 했습니다. 게다가 현대 계열은 노조가 강성이기도 했구요.

 

 

하이닉스의 간단한 역사

SK하이닉스는 1983년 현대전자산업으로 출발했습니다. 이후 1999년 정부 주도의 "빅딜"을 통해 LG반도체를 합병하면서 단숨에 세계 2위 DRAM 제조사로 올라섰어요.
그런데 거기서부터가 시련의 시작이었습니다. 합병 후 과도한 부채에 시달리다가 2001년 현대그룹에서 분리됐고, 오랜 채권단 관리 체제에 들어가게 됩니다. 이때 "현대"라는 이름이 오히려 기업 이미지에 걸림돌이 된다는 판단 하에 하이닉스반도체로 사명을 바꿨죠.
한 때, 주가가 한 주에 100원대까지 떨어지면서 상장 폐지 얘기도 심심치 않게 나왔습니다.

 

 

반도체 업계의 간단한 역사와 치킨 게임

아래 그림은 Gemini 가 그려준 시대별 DRAM 반도체 시장점유율입니다. 얼마나 정확한지는 잘 모르겠지만, 대충 엇비슷해 보입니다.

1990년대 말까지만 해도 DRAM 생산업체가 전세계에 20개가 넘게 있었습니다. 

대충 1998년 IMF 무렵 전후해서 그 수가 절반 정도로 줄었고, 그 다음 2008년 리만 브라더스 사태 전후로 다시 절반정도로 줄어듭니다.

2010년대 기준으로 보면, DRAM 반도체 시장은 독과점이라고 봐도 과언이 아닙니다. 이 무렵부터 삼성과 하이닉스 가 전체 시장점유율의 70% 정도 혹은 그 이상을 가져갑니다.

 

 

위 그림처럼 독과점이 되어가는 과정에서 빠질 수 없는 게 바로 <치킨 게임> 입니다.

본래 <치킨 게임> 은 두 사람이 절벽을 향해 차를 몰고 가다가 먼저 뛰어내리는 사람이 겁쟁이가 되는 게임입니다. 

아래 동영상 - 이유없는 반항, 제임스 딘, 1955 - 이 <치킨 게임> 을 보여주는 장면입니다.

 

https://www.youtube.com/watch?v=BGtEp7zFdrc

 

 

반도체 업계에서 치킨 게임이란, 공급이 넘쳐서 가격이 폭락해도 생산을 줄이지 않고 오히려 더 쏟아내는 전략을 말합니다. 

손해를 보면서도 생산을 하게 되면 결국 회사가 망해버릴 수도 있습니다.

이런 모습은 위 영화에서 자신이 죽을수도 있는데 끝까지 핸들을 돌리지 않는 등장인물들과 닮아있습니다.

 

한 편으로 생산을 줄이지 않는 이유 중 하나는  반도체 생산 공정을 멈추는 것도 쉽지 않기 때문입니다.
- 이런 산업들이 종종 있습니다. 아무 것도 생산하지 않더라도 생산 라인을 계속 돌려야 합니다.
- 대표적인 곳이 제철소인데, 용광로가 꺼져버리면 다시 사용할 수 없기 때문에, 제품이 나오지 않더라도 계속 용광로의 온도를 유지해야 합니다.

 

 

그런데, 이 미친 짓을 지난 수 십년 동안 몇 번을 했습니다. 그리고, 살아남은 회사들은 독과점이 되어 꿀을 빨게 됩니다.

실제로 하이닉스의 경우 거의 망하기 일보직전까지 가기도 - 위에서 한 번 언급했지만, 동전주(동전으로 살 수 있는 주식)가 되기도 - 했습니다.

 

요즘 SK 의 하이닉스 인수를 대단한 결정이라고 얘기하는 친구들이 있던데, SK 의 하이닉스 인수는 이 업계에서는 이미 삼성전자와 하이닉스가 평정한 상태였고, 독과점 회사 중 하나를 인수한 것 뿐이라고 봅니다. SK 는 항상 이런 회사들을 인수해왔습니다. 첫 번째가 대한석유공사(예전에는 "유공"이라고 불렀음, 현 SK 이노베이션) 이었고, 두 번째가 한국이동통신(현 SK 텔레콤) 이었습니다. 둘 다 독과점 시장의 정부 공기업을 잘 인수(? - 심지어, 대통령 딸과 결혼해서) 한 사례입니다. 차이는 이 두 가지 사례는 국내 독과점 기업을 정부로 부터 인수한 것인데 반해, 하이닉스의 경우,  전세계를 상대로 한 독과점 기업을 채권단으로 부터 인수한 것입니다.
좀 과장해서 얘기하면, IMF, 리만 사태때 반즈음 죽어버릴 뻔한 회사를 임직원들이 절치부심해서 겨우 살려 놓았더니, SK는 인수해서 꿀만 빤 셈입니다.

 

 

오너 경영이 좋을까, 전문경영인이 좋을까

이 두 회사의 역사에서 오너 경영 논쟁의 흥미로운 사례가 하나 나옵니다.


2007~2008년, 삼성전자는 이건희 회장의 비자금 사건으로 한바탕 시끄러웠습니다. 

김용철 변호사의 폭로로 불거진 이 사건은 특별검사 수사로 이어졌고, 1,200여 개 차명계좌에 4조 5,000억 원 규모의 차명재산이 드러났습니다. 

이 회장은 조세포탈 혐의로 기소되면서 경영 일선에서 물러나게 됩니다.
문제는 이 시기가 공교롭게도 반도체 치킨 게임의 한복판이었다는 점입니다.

오너 부재 상황에서 삼성전자 경영진은 신규 반도체 라인 투자를 보류합니다.

수익이 악화된 상황에서 당시 매출(30~40조 원 규모) 대비 상당한 규모의 투자 결정을 오너 없이 내리기 어렵다는 이유였죠.

 

이에 반해, 채권단의 감시 아래 전문경영인이 경영하던 하이닉스는 대규모 유상증자를 단행합니다.

Google Gemini 는 이 때 유상증자를 이렇게 평가하네요.

하이닉스(현 SK하이닉스)는 2000년대 후반, 특히 금융위기 직후인 2009년 극심한 자금난을 타개하기 위해 두 차례 대규모 유상증자(1월, 5월)를 단행했습니다. 당시 약 1조 원 이상의 자금을 확보하여 부도 위기를 넘겼으며, 이 유상증자는 2009년 당시 큰 주목을 받은 대규모 조달 건으로 기록되었습니다.

2009년 하이닉스 유상증자 

주요 내용배경 : 2000년대 후반 반도체 불황과 금융위기, 경영난 심화로 인한 생존 위기.

- 1차 증자 (2009년 1월): 약 3,240억 원 규모.
- 2차 증자 (2009년 5월): 약 7,000억 원~7,245억 원 규모 (주당 발행가 1만 350원).

결과 : 대규모 자금 확보로 2000년대 위기를 극복하고, 이후 SK그룹 인수 등으로 이어지는 기반을 마련.
당시 유상증자는 주가 폭락 상황에서 진행되었으나, 이후 주가 회복과 성공적인 인수로 인해 역사적인 '동전주' 탈출과 부활의 신호탄이 되었습니다.

 

 

이건희 회장은 이후 법적 리스크가 해소되자 경영에 복귀하면서 기존 투자 계획을 두 배로 늘려 공장을 증설했습니다.

결단력 있는 오너 경영의 좋은 사례라 볼 수도 있지만, 한편으로는 오너 리스크 때문에 결정이 늦어진 측면도 있습니다.
하이닉스 사례를 보면 굳이 오너가 아니어도 큰 결정을 내릴 수 있다는 걸 보여줬고, 그 점에서 오너 리스크가 오히려 더 클 수 있다는 반론도 충분히 성립합니다.

 

이런 과정을 거쳐, 삼성전자와 하이닉스는 2010년대에 들어서서 전 세계 시장의 나눠 먹게 됩니다. (좀 과장되게 말하면, 삼성전자와 하이닉스가 전세계 시장을 반반씩 나눠 먹습니다. 대충 삼성전자가 4~50%, 하이닉스가 3~40% 정도의 시장점유율이었던 것으로 기억합니다.)

 

하이닉스의 SK 인수 이후

하이닉스 인수 이후 SK 는 이 바닥의 인력을 모조리 흡수합니다.

이것은 SK 가 하이닉스를 인수한 뒤, 현재를 만들어낸 원동력 중 하나입니다.

이 때 느낌이 삼성전자가 2000년대에 쓰던 방법과 비슷했는데, 이쪽 전공자들은 거의 다 쓸어가는 식이었습니다.

경력직도 많이 뽑았는데, 삼성에서 일하던 친구들이 하이닉스로 이직도 많이 했었던 것 같습니다.

- 동일 업계(경쟁사)로의 이직을 제한하는 법이 있는 걸로 아는데, 뭐 알다시피 이렇게 저렇게 잘 회피해서 이직을 하지 않았을까 싶습니다.

 

삼성 인사 담당자들 이야기를 들어보면, 직원이 퇴사 의사를 밝혀도 굳이 붙잡지 않는 문화가 있는 것 같습니다.

"삼성에서 일할 사람은 많다"는 자신감이기도 하고, 이병철 회장 시절부터 이어온 사업보국(事業報國) 정신, 즉 "사업으로 나라에 보탬이 된다"는 마인드가 깔려 있기도 합니다.

잘 길러낸 인재가 나가서 한국 산업에 기여한다는 논리죠. 그래서, 이직을 쉽게 했던 것 같기도 합니다.

 

SK가 하이닉스를 인수한 직후만 해도 아직 2등 기업의 느낌이 강했습니다.

게다가 당시는 "초격차" 를 부르짖던 넘사벽 삼성전자 CEO 들이 있던 시기이기도 했구요.

무어의 법칙(Moore's Law)은 반도체 집적회로의 성능과 트랜지스터 집적도가 약 18~24개월마다 두 배씩 증가한다는 경험적 관측이자 예측인데, 이 예측은 십수년간 이어져 왔는데, 삼성전자 황창규 사장은 황의 법칙(Hwang's Law) - 반도체 메모리 용량이 1년마다 2배씩 증가한다 - 에 의해 여지없이 깨졌습니다. 그리고, "초격차"라는 용어를 주장한 권오현 사장이 황창규 사장의 뒤를 이었습니다.
이 시기에 서양애들은 이 말도안되는 개발 속도를 놓고, 삼성전자에서 외계인을 고문해서 메모리를 만든다고도 했었습니다.

 

하이닉스가 업계 1위를 목표로 했던 전략은 유효했다고 볼 수 있습니다.

물론, 여전히 DRAM 시장점유율은 삼성전자가 1위입니다.

DRAM 에서 1위를 하지 못했지만, HBM 같은 제품은 1위로 올라섰으니까요.

 

Update 2026. 5. 
삼성전자가 파업에 대처하는 자세는 너무 안이하고 좁은 시야를 보여줍니다.
물론, 이병철 회장대에서부터 무노조경영을 자랑삼아 이야기하던 회사이니,
파업에 대한 대처 방식이 아무래도 경험이 적을 수도 있습니다.
하지만, 단순히 언론을 통한 플레이나 노조가 요구하는 사항에 대해 답변하는 태도를 보면 매우 아마추어 같다는 생각이 듭니다.
단순히 이번 파업은 급여의 문제가 아니라 인력 유출의 문제로 봐야하고, 기존의 성과급 제도의 재편을 심도깊게 탐구해 봐야하는 상황입니다.


 

뱀다리 1. 필자는 삼성전자와 하이닉스의 소액 주주입니다. 엄청나게 큰 금액을 투자한게 아니라서 딱히 더 엄청 많이 잘 되라고 쓴 글은 아닙니다. 그러니까.... 그래도 ... 어디든 잘 되면 좋습니다. ;;;;

 

'Misc' 카테고리의 다른 글

Misc 카테고리에 관하여  (0) 2026.05.04

 

이 카테고리에 올리는 글은 AI 의 도움을 받기는 하지만,

 

AI 가 절대로 작성할 수 없는 글들을 올릴 생각입니다.

 

 

GitHub Actions에서 cron 설정은 일정한 시간 간격으로 워크플로우를 실행할 수 있도록 해주는 옵션입니다.

이를 사용하여 주기적으로 작업을 실행하거나 스케줄에 맞게 작업을 수행할 수 있습니다.

 

Linux 의 crontab 에서 가져온 것으로 보이는데, 이미 crontab 을 사용해보신 분이라면 설정 방법이 같기 때문에 어렵지 않을 것입니다.

cron 설정은 on 항목 아래에 추가됩니다. 아래는 cron 설정의 예제입니다:

 

on:
  schedule:
    - cron: '0 0 * * *' # 매일 00:00에 실행


위의 예제에서 cron 설정은 매일 00:00(자정)에 워크플로우를 실행하도록 합니다.

cron 표현식은 다음과 같은 형식을 따릅니다:

 

* * * * *


각 ' * ' 는 다음과 같은 시간 단위를 나타냅니다:

1. 분 (0-59)
2. 시 (0-23)
3. 일 (1-31)
4. 월 (1-12)
5. 요일 (0-6, 일요일부터 토요일까지)

 

' * ' 은 보통 모든 경우를 의미하지요. 

자주 사용되는 일반적인 cron 설정 예시는 다음과 같습니다:

1. 0 * * * *  # 매 시 정각마다 실행, 0분 매시간 매일 매월 모든요일

2. 0 0 * * *  # 매일 자정에 실행, 0분 0시 매일 매월 모든요일
3. 0 0 * * 1 # 매주 월요일에 실행, 0분 0시 매일 매월 월요일

 

 

UTC 기준

GitHub Actions에서 cron 설정은 UTC 기준으로 작동합니다. 따라서 지역 시간대를 고려하여 설정해야 할 경우 해당 시간대의 UTC 시간을 계산하여 설정해야 합니다.

 

예를 들어, 한국 시간 기준으로 월요일 오전 3시에 실행되도록 cron 설정을 작성하려면 UTC 시간을 기준으로 설정해야 합니다. 한국 시간은 UTC+9 시간대이므로 UTC 시간에서 9시간을 더한 값을 사용해야 합니다.

월요일 오전 3시는 UTC 시간 기준으로는 일요일 오후 6시에 해당합니다. 따라서 아래와 같이 cron 설정을 작성할 수 있습니다:

 

on:
  schedule:
    - cron: '0 18 * * 0' # 매주 일요일 18:00 (UTC)에 실행

 

위의 설정은 한국 시간으로 월요일 오전 3시에 해당하는 시간에 워크플로우를 실행하게 됩니다.

 

 

 

참고

'workflow_dispatch' 를 설정해두면, 수동으로 실행할 수 있으며, 'push' 설정을 해두면, 어떤 브랜치나 혹은 특정파일이 push 될 경우에 실행하게 할 수도 있습니다.

 

 

on:
  workflow_dispatch:  
  schedule:
    - cron: '0 0 * * 0' # 매주 일요일 00:00 (UTC)에 실행  
  push:
    branches:
      - master # 특정 branch
    paths:
      - 'src/main/kotlin/Main.kt' # 특정 파일

 

이 글은 Firebase Authentication - Google Login 시 발생할 수 있는 에러를 해결하면서 마주칠 수 있는 사안에 대해 기술한 글입니다.

아래 그림은 Firebase Authentication SignIn 관련 코드를 추가했을 때 나오는 화면이며, SignIn 시 에러 코드를 발생시키며, 더 이상 진행되지 않을 때입니다.

UX 에서 딱히 어떤 에러 상황을 보여주지 않기 때문에, (로그인에 실패하였습니다! 팝업같은) 코드가 제대로 들어간 것인지 혹은 서버의 문제인 것인지 처음 닥쳤을 때는 조금 당황할 수 밖에 없습니다.

참고로 출처는 구글 코드랩 (https://firebase.google.com/codelabs/firebase-android?hl=ko#5) 입니다.

Firebase SignInActivity

 

 

0. 문제의 원인

Android SHA-1 FingerPrint 부재

앱을 플레이스토어에 올리려고 할 때 필요한 '나의 시그니처(사인)' 이 없어서 입니다.

<이건 내가 만든 거야 그러니깐 나중에 업데이트할 때도 이 사인을 보고 내가 한 게 맞을 때 업데이트 하라고!> 얘기해주는 것이조

 

1. 이제 시작인데, ...

처음 앱을 만들 때에는 보통 이런 사인 없이도 테스트해볼 수 있습니다.

왜냐하면, <안드로이드 스튜디오> 에서 기본 값을 넣어주기 때문입니다.

 

이 때 사용하는 키스토어는 .android/debug.keystore 입니다.

MS윈도우와 Mac 의 기본 저장위치가 조금 다르니 아래 코드를 참고하세요. storepass, alias, keypass 는 동일합니다.

 

keytool -list -v -keystore c:\Users\<%mylogin%>.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
Alias name: androiddebugkey
Creation date: 2023. 3. XX.
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: C=US, O=Android, CN=Android Debug
Issuer: C=US, O=Android, CN=Android Debug
Serial number: 1
Valid from: Fri Mar XX 16:57:34 KST 2023 until: Sun Mar XX 16:57:34 KST 2053
Certificate fingerprints:
	 SHA1: 3F:EA:C9:19:DC:AD:3E:EE:3F:C1:2B:BB:71:B8:84:10:
	 SHA256: D4:5D:DB:FA:A2:61:05:AF:EF:66:B8:61:A4:4F:10:89
Signature algorithm name: SHA1withRSA (weak)
Subject Public Key Algorithm: 2048-bit RSA key
Version: 1

Warning:
The certificate uses the SHA1withRSA signature algorithm which is considered a security risk.

 

 

firebase console(https://console.firebase.google.com/) 에서 프로젝트를 선택한 뒤, 프로젝트 설정을 선택합니다.

그리고 설정 화면에서 맨 아래로 내려가면, 내 앱> 앱 추가 를 선택하여 패키지명과 FingerPrint 값(위 코드를 실행시켰을 때 나오는) 을 입력해 주면 됩니다.

 

 

 

 

2. 이 시점에서 키스토어를 만드시는 것도 ...

Android Studio > Build > Generate Signed Bundle / Apk > Next > 중간에 Create New 버튼을 누르시면 키스토어를 만드실 수 있습니다.

 

 

그리고, 만들어진 keystore 에서 나온 FingerPrint 값을 Firestore 에 등록해 놓고, 아래와 같이 build.gradle 파일에서 해당 signingConfig 를 debug 에서 사용하는 것도 나쁘지 않은 방법입니다.

실제로 저도 실서비스에서만 발생하는 버그를 찾기위해서 아래와 같은 방법을 종종 사용하곤 합니다.

 

    buildTypes {
        debug {
            //signingConfig signingConfigs.release
        }
        release {
            signingConfig signingConfigs.release
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    signingConfigs {
        release {
            storeFile rootProject.file('key.store')
            keyAlias 'key1234'
            keyPassword '123456'
            storePassword '123456'
        }
    }

 

3. 덧붙임

테스트 중이 "Code : 7" 에러가 나왔던 적이 있었던 것 같은데, 정확하지는 않지만, SignUp 이 제대로 되지 않아서 발생했던 것으로 기억합니다. 참고하시길~

 

 

발단

 

Google PlayStore 의 최근 요구 사항에 따라 ( https://developer.android.com/distribute/best-practices/develop/target-sdk?hl=ko ) 최근 App targetSdkVersion 을 30으로 올리는 작업을 진행했습니다.

 

2021년 8월부터 신규 앱은 다음 요건을 충족해야 합니다.
...
 * API 수준 30(Android 11) 이상을 타겟팅하고 동작 변경사항에 맞게 조정합니다.
...

 

진행하다 보니, 평소에 잘 동작하던 queryIntentActivities 가 제대로 동작하지 않았습니다.

 

 

개요

 

PackageManager.queryIntentActivities 는 보통 암시적 Intent ,(이를 테면, URL 을 가지고 Web Browser 로 넘기기전에 적당한 Intent ), 를 실행하는 App 을 선택하기 위해, 주로 사용하는 API 였습니다.

 

안드로이드 예전 버전에서 startActivity 를 실행했을 때, 생길 수 있는 선택창을 없애기 위한 방법 중 하나였습니다.

이를  테면, 브라우저 앱이 크롬, 파이어폭스, 제조사 브라우저 등 여러개 설치되어 있다고 할 때,

URL - ACTION_VIEW Intent 로 startActivity 를 실행하기 전, 선택가능한 Activity 중 하나를 코드상에서 선택하게 함으로,

사용자에게 어떤 브라우저를 사용할 것인지 확인을 받는 동선을 없애버려서, 좀 더 편하게 사용하게 하려는 의도이기도 합니다.

 

혹은 startActivity 를 사용할 때, ActivityNotFoundException 을 회피하기 위해 사용하기도 했습니다.

아래 코드 처럼 try-catch 로 처리하거나, Intent.resolveActivity 를 사용하는 방법도 있긴하지만,

queryIntentActivities() 결과 값 크기로 구분하기도 했습니다.

 

    try {
        startActivity(intent)
    } catch (e: ActivityNotFoundException) {

    }

 

이 API 를 잘 이용해서, action = MAIN, category = LAUNCHER 로 인텐트 필터로 사용하면, 앱 관리자 같은 앱을 만들 수도 있습니다.

 

 

좀 더 깊숙히

 

하지만, targetSdkVersion = 30 부터는 이런 동작이 쉽지 않게 되었습니다.

예를 들어, 아래와 같이 jpeg image 를 보내는 intent 를 만들었다고 가정하면,...

 

fun Context.queryIntentActivitiesTest() {
    val jpegIntent = Intent(Intent.ACTION_SEND).apply {
        this.setDataAndType(Uri.parse("file://a"), "image/jpeg")
    }
    val query = this.packageManager.queryIntentActivities(jpegIntent, 0)
    val system = query.filter { it.activityInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0 }
    val other = query.filter { it.activityInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0 }
    Log.d("Test", "jpegIntent queryIntentActivities = total, SYSTEM, NON-SYSTEM " +
            "${query.size}, ${system.size}, ${other.size}"
    )
}

 

이 결과는 아래 표와 같이 나옵니다.

  Total System Other
targetSdkVersion = 29 42 14 28
targetSdkVersion = 30 15 7 8

 

즉, targetSdkVersion 을 변경한 것으로 queryIntentActivities 로 반환된 결과 값의 차이가 생기고,

이는 실행할 수 있는 Activity 의 갯수가 줄어든다는 것입니다.

 

물론, 단순히 startActivity 만 사용하는 코드에서는 전혀 문제가 없지만, 위에서 언급했던 것처럼, 특정 App 이 직접 실행되도록 하는 코드를 작성할 경우에는 targetSdkVersion 만 변경한 것으로도 이전과 다른 동작을 하게 될 수가 있습니다.

 

구글이 targetSdkVersion = 30 에서 이런 제한을 둔 것은 보안상의 이유라고 보여집니다.

위에서도  언급했지만, queryIntentActivities() 를 잘 이용하면, <앱 관리자> 유형의 앱을 개발할 수 있습니다.

즉, 이 API 를 이용하면, 사용자 단말에 설치되어 있는 앱 목록을 알 수 있다는 것이고, 이를 다른 용도로 앱이 이용할 가능성이 있기 때문에, API 이용에 제한을 두기 시작했다는 것으로 볼 수 있습니다.

 

 

해결방법

 

만약, targetSdkVersion = 29 일 때와 동일하게 동작하게 하려면,

아래와 같이 AndroidManifest.xml 파일에 아래와 같이 <queries></queries> 를 추가해주어야 합니다.

 

<manifest>
    <queries>
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="image/jpeg" />
        </intent>
    </queries>
    
    <application>
    </application>
</manifest>

 

혹은 아래와 같은 permission 을 추가해 주어야 합니다. 하지만, 이 방법은 권장?, 추천?하지 않습니다.

 

<manifest>
	<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
	<application>
	</application>
</manifest>

 

왜냐하면, <앱관리자> 같은 특정한 성격의 앱이 아니고서는 플레이스토어 심사를 통과하지 못 할 가능성이 높기 때문입니다.

( https://support.google.com/googleplay/android-developer/answer/10158779?hl=ko  )

 

위 문서에는 아래와 같이 언급되어 있습니다.

 

앱이 QUERY_ALL_PACKAGES 권한의 허용되는 용도에 관한 요구사항을 충족할 경우, Play Console의 권한 선언 양식을 사용해 이 권한 및 위험성이 높은 기타 모든 권한을 선언해야 합니다.

정책 요구사항을 충족하지 못하거나 권한 선언 양식을 제출하지 않으면 앱이 Google Play에서 삭제될 수 있습니다.

중요: 앱에서 제한된 권한을 사용하는 방식을 변경하려면 정확하게 업데이트된 정보로 요청을 수정해야 합니다. 이러한 권한을 사기성 및 선언되지 않은 용도로 사용하면 앱이 정지되거나 개발자 계정이 해지될 수 있습니다.

 

 

참고한 문서

 

1. Google Play의 타겟 API 수준 요구사항 충족하기

  -  https://developer.android.com/distribute/best-practices/develop/target-sdk?hl=ko

2. 패키지 공개 상태 관리

  -   https://developer.android.com/training/basics/intents/package-visibility

3. 폭넓은 패키지(앱) 가시성 (QUERY_ALL_PACKAGES) 권한 사용

  -   https://support.google.com/googleplay/android-developer/answer/10158779?hl=ko 

 

#Android,#PackageManager,#targetSdkVersion=30,#queryIntentActivities

주의! 아래 글을 Github Copilot 이 베타테스트를 하던 무렵에 쓰여진 글입니다.
Github Copilot 사용 방법은 https://docs.github.com/ko/copilot/quickstart 을 참조해주세요.
베타테스트 시절의 사용기라 하더라도 후기는 그럭저럭 참고하실만 할 것 같습니다.

 

Github Copilot 은 VSCode 혹은 IntelliJ 에서 코드를 자동으로 작성하도록 도와주는 AI 도구입니다.

추천해주는 코드들은 Github 에 공개되어 있는 코드 기반의 결과물이라 GPL 라이선스 코드가 들어갈 수 있다는 논란이 있습니다.

그럼에도 대체 Copilot 이란 놈은 어떤 녀석인지 호기심이 발동하여 설치해보았습니다.

 

0. Copilot 신청 및 설치

Copilot Homepage (https://copilot.github.com/) 에서 SignUp 을  클릭 후,

아래 화면에서 github 로그인, 약관 동의를 하고, 한동안 잊고 있으면, Copilot 을 설치하라는 메일이 옵니다.

 

https://copilot.github.com/
SignUp > github 로그인 후 동의 화면
마지막 화면

 

저도 언제 신청을 했었는지 완전히 잊어버렸기 때문에 얼마나 대기시간이 필요한지는 잘 모르겠습니다.

못해도 2~3 달 정도는 걸리지 않았나 싶습니다.

 

Github Copilot 메일

 

이런 메일을 받고난 뒤, Install  GitHub Copilot 을 클릭하면, https://github.com/github/copilot-docs  으로 이동합니다.

 

https://github.com/github/copilot-docs &nbsp;

 

참고로 저는 IntelliJ - Kotlin 사용자로, Plug-in 설치 이후, Github 로그인을 해주면, Plug-in 을 활성화 할 수 있습니다.

 

아래는 10분사용기 입니다.

 

1-0. 의도

스트링 파일명에 바로 텍스트를 덧붙이는 함수를 만드려고 했습니다.

 

1-1. 타이핑한 코드

fun String.append2File(s:String)

 

1-2. 결과 - 추천해준 코드

 

 

2-0. 의도

위 코드는 약간 아쉬웠습니다. appendln() 같은 느낌이 필요했습니다.

그래서, 스트링의 마지막을 보고 개행문자가 아니면 개행문자를 추가하려고 했습니다.

 

2-1. 타이핑한 코드

fun String.append2File(s:String){
    s.last()
    val file = File(this)
    file.appendText(s)
}

 

2-2. 결과 -  추천해준 코드

 

 

3. 마치며

append2File() 을 만들때는 그래도 뭐랄까 함수명을 잘 정한건가 싶다는 생각이 들었는데,

한편으로는 last() 를 사용하는 경우에, 개행문자를 추가하는 코드들이 많아서 일지도 모르겠다는 생각이 들기는 합니다.

심지어 코드에 오류가 있음에도 불구하고, s.last() 를 타이핑하고 나서는 마치 제 생각을 읽고 있는 것 같아서 많이 당황했습니다.

 

 

4. 덧붙임

4-1. 주석 부분도 추천해줍니다.

4-2. 주석을 잘 써놓으면, data class 그냥 만들어 줍니다.

아래 그림은 주석을 써놓은 뒤, "data class Human"  을 타이핑하자 추천해준 코드

 

4-3. 코딩을 글쓰기에 비유하자면, 글에서 기-승-전-결 이 있는 글을 쓴다고 하면,

기-승 정도를 타이핑하면, 전-결 에 해당하는 코드를 추천해줍니다.

 

 

어느 날, 일련의 데이터를 간단한 텍스트 파일로 저장하려고 알아보다 보니, CSV 란 녀석이 있다는 것을 알게 되었습니다.

근본이 없는 인간인지라 DB 로 저장할까 하다가, 얼마 되지도 않는 데이터를 DB 까지 쓰는 건 좀 그렇다 싶어서,

'간단한 방법들은 무엇이 있을까?' 하고 찾아보다 보니, CSV 라는 녀석이 있었습니다.

 

1. CSV 란?

 

CSV는 C-Comma, S-Seperated, V-Values or Variables 를 뜻하는 말로,

데이터를 Comma( , )  로 구분해서 사용하는 파일입니다.

흔히 구분자 - Delimiter - 를 Comma ( , )  로 사용하는 것입니다.

일부에선 Comma ( " , " ) 대신 Tab ( "\t" ) 을 사용하거나, 스페이스 ( " " ) 를 사용하기도 해서, Character-Seperated Values 라고 불리기도 합니다.

보통 MS-Excel 이나 맥의 Numbers 같은 스프레드 시트 프로그램에서 불러들여 읽고 쓸수 있습니다.

 

'일련의 데이터' 를 저장한다고 했는데, <번호>, <이름>, <점수> 와 같은 간단한 정보 50개 정도였습니다.

그 예제는 아래와 같습니다. 보시다시피, 한 줄이 하나의 레코드를 의미합니다.

 

1,홍길동,50
2,임꺽정,60
3,김삿갓,100

 

여기서, 한 가지 의문이 생길 수 있습니다.

만약, 구분자로 Comma( " , " ) 를 사용한다면, 데이터 안에 Comma( " , " ) 는 어떻게 표시하지?

- 이 때는 따옴표 ( " \" " )를 사용합니다. 예제는 아래와 같습니다.

 

1,"홍,길동",50
2,임꺽정,60
3,김삿갓,100

 

위와 같은 사례만 보아도, CSV 포맷이 데이터 오염에 취약한 편이라고 할 수 있겠습니다.

그럼에도, 이 파일 포맷이 여전히 애용되는 이유는 데이터의 크기가 작기 때문입니다.

그리고, 입력되는 데이터가 명확하다면, (예를 들어, 데이터 안에 CRLF, Comma( " , " ), Quote ( " \" " ) 가 없다면,)

Parser 를 구현하는 것은 매우 간단, 단순하여, 굳이 잘 만들어진 Library 를 찾지 않아도 됩니다.

 

 

2. 실사용

 

(1) JAVA Library

 

- Apache Commons CSV : https://commons.apache.org/proper/commons-csv/

- OpenCSV : http://opencsv.sourceforge.net/

- Super CSV : http://super-csv.github.io/super-csv/

 

뭐랄까 'Open' 이란 키워드가 붙어있으면, 더 믿음직해보여서 일단 한 번 사용해봤고,

github 에서 검색해서 나온 결과값으로 생각보다 괜찮아 보여서 사용해봤는데,

이 조그만 스펙을 가지고도 예외처리와 사용법을 익히다 보니,

의외로 라이브러리를 잘 안 찾게 되어, 뭐랄까 추천하기는 살짝 애매합니다.

그냥 프로젝트 스펙에 맞춰 적당한 방법 혹은 라이브러리를 사용하시는 것이 무난하리라 봅니다.

 

 

(2) CSV Editor

 

본래 목적은 Excel 같은 파일에서도 조금 쉽게 편집해서 데이터를 추가한 뒤,

이 파일을 코드로 별도의 작업을 하려던 것이었는데, 의외로 MS Excel 은 한글을 가져오는데 약간의 문제가 있었습니다.

그리고, MS Excel 은 csv 확장자 파일의 기본 구분자(seperator)가 탭("\t") 이기 때문에, 별도의 작업을 해주어야 했습니다.

뭐랄까 살짝 귀찮아지는...

 

그런 측면에서, 개발툴에서 제공하는 플러그인을 이용하는 것은 괜찮은 방법입니다.

 

intelliJ IDEA CSV Plugin 화면

 

intelliJ IDEA CSV Plugin 사용 화면 : Text Editor 옆에 탭으로 Table Editor 가 추가되어 있어 사용하기 편리, 행마다 다른 글씨색은 덤

 

 

3. 마치며

 

별도의 CSV 스펙으로 '#' 을 주석으로 처리합니다.

 

현재는 '#' 을 주석처리하는 코드를 만들기가 귀찮아서, Apache Commons CSV 를 사용하고 있습니다. ㅎㅇ -_-;;

 

 

 

 

Google Firebase 는 혼자 앱 개발을 할 때 필요한 것들을 무료로 제공해줍니다.

이 중 혼자 개발 할 때 가장 유용한 것 중 하나는 TestLab 입니다.

TestLab 은 하루에  가상기기 10 대, 실제기기 5대에서 테스트를 해 볼 수 있습니다.

스크립트를 사용해서 자신이 원하는 테스트를 해볼 수 도 있지만, Robo - 자동 테스트를 보통 사용합니다.

 

테스트 실행 > Robo

 

apk 혹은 aab 를 업로드 하면 테스트를 진행할 수 있습니다.
필터링 을 선택하면, 제조사 혹은 API Level 로 구분해서 선택할 수 있습니다.

 

물론, 테스트에 필요한 기기가 조금 아쉬울 때가 있긴 합니다. 많은 듯 하면서, 그렇게 많지 않습니다.

하지만, 무료로 제공해주는 기능이기 때문에 그렇게 불만은 없습니다.

구글 레퍼런스 폰 계열의 다른 API Level, 가상기기 정도만 제공해 주어도 만족합니다.

 

테스트를 마친 뒤 결과 화면, 각각 클릭해서 들어가면, 스크린 샷과 테스트 동영상을 확인할 수 있습니다.

 

Firebase TestLab 은 Google PlayStore Publisher 들에게 제공하는 <사전 출시 보고서> 와 동일합니다.

자동 스크립트는 View 에 설정된 Key Event 혹은 OnClick Event 에 따라 모든 UX 경로를 실행하려고 시도합니다.

그러므로, 출시 이전에 미리 예상치 않은 Exception 혹은 Error 가 발생하는지 확인해 볼 수 있습니다.

 

이 자동 스크립트는 약간 비효율적일수는 있지만, 자동으로 도는 것인데다,

간혹, 예상치 않았던 경로를 찾아내기도 하기 때문에, 혼자 앱을 개발하는 개발자에게는 매우 유용한 도구 입니다.

 

 

#Firebase, #GoogleFirebase, #FirebaseTestLab, #GoogleFirebaseTestLab