스토리 홈

인터뷰

피드

뉴스

조회수 650

보고도 인터페이스다

직장 생활의 8할은 보고다.물론, 실제 보고는 2할에도 못 미치겠지만,자신이 평가되는 지점의 8할 이상이 보고에서 이루어진다.그래서많은 사람들이 억울해한다.자신은 열심히 일 했는데, 보고를 잘 못한 탓이라고...보고 또 보고특히나 직급이 올라갈수록 보고의 비중과 중요성은 높아진다.얼마나 일을 잘 했는지는, 노력의 과정이 아닌보고의 노련함에서 좌우되는 경우가 많다.마주하기 어려운 의사결정자를 앞에 두고 하는어려운 보고 자리뿐만 아니라,매일 접하는 상사에게 하는 일상적인 보고도 마찬가지다. 많이 알고 있는 것과 보고를 잘 하는 것은 별개다.대부분 보고에 실패하는 것은자신의 관점에서 보고하기 때문이다.보고를 받는 사람을 고려하지 않기 때문이다. 전혀.보고라는 것이자신의 수고의 결과와 상사가 얻고자(알고자) 하는 것 이에서 이루어지는 접점, 즉 Interface라는 사실을이해한다면, 여기에 UI(User Interface)의 원칙이 적용되어야 한다.당신과 상사와의 Interface가 '보고'의 형태일 뿐이다즉, 내가 말하고 싶은 것이 아니라,듣는 사람이 듣고자 하는 것이 핵심이다.유시민 작가가 다른 많은 지식인과 다른 점은무언가를 더 많이 알고 있기 때문만은 아니다. 상대방의 관점을 이해하면서 그 눈높이에 맞춰 이야기하기 때문이다.귀에 쏙쏙 이야기가 꽂히는 이유이다.직장인들이 흔히 보고에 실패하는 경우는 다양하겠지만,여기 자주 보이는 3가지 요인을 살펴보겠다.1. 보고 목적이 불분명한 경우대부분 보고를 실패하는 첫 번째 요인은보고의 목적을 명확히 인지하지 못하는 경우이다.여긴 어디? 나는 누구?보고의 목적이 새로운 정보를 전달하기 위한 것인지,자신의 주장을 설득시키기 위한 것인지,의사결정을 받기 위한 자리인지,단순히 의견을 듣고자 하는 자리인지 혼동하는 사람은보고의 핵심이 사라지고 논점을 쉽게 놓치기 쉽다.한마디로 '횡설수설'하게 된다.2. 나만큼 알고 있을 것이라는 착각두 번째 경우는,보고받는 사람이 자신과 동일한 정보를 가지고 있다는 착각 때문이다.이 정도는 알고 있겠지?보고해야 할 내용을 얘기하기 전에보고 받는 사람이 어디까지 알고 있는지, 이 보고 준비가 어떤 정황에서 이루어졌는지 배경을 공감시키거나 적어도 리마인드 시켜줄 필요가 있다.양자가 같은 페이지에 서 있는지를 확인하는 과정이 반드시 필요하다. 그래야 보고가 간결해지고, 놓치고 지나가는 부분을 미리 확인할 수 있다. 혼자 신나서 진도를 뺄 경우에는, 듣는 이는 뭔 소리인가 싶을 경우가 많을 수밖에 없다.3. 너무 상세하게 보고하는 경우세 번째 경우는,고민했던 내용을 지나치게 상세하게 보고할 경우이다.모든 상사는 기본적으로 '결론'만 듣고 싶어 하는 경향이 있다. 장황하면 지는 거다. 그래서 결론이 뭔가?판단하기 어려운 복잡한 문제들일 수록 간추린 내용을 듣고 싶어 한다.하나로 결론 내리기 어렵다면, 우선순위를 압축하는 수고를 해야 한다.준비하는 과정에서 모든 경우의 수를 일일이 보고하는 것은그만큼의 수고를 어필할 수는 있겠지만, 듣는 이의 입장에서는 답답할 뿐이다.상사는그 수고를 대신하여, 자신의 판단을 심플하게 해주는직원이 이뻐 보일 수밖에 없다.Communication Cost가 높은 조직일수록생산성과 업무 효율성이 낮아진다.신경세포의 뉴런이 말초 신경의 자극을 뇌에 빠르고 올바르게 전달하지 못한다면, 뇌는 신체에 적절한 대응을 지시하는 타이밍을 놓치거나 잘못된 결정을 내리게 된다.그런 측면에서 '보고'는 bottom up의 중요한 Interface일 뿐만 아니라,Communication의 흐름을 좌우하는 조직의 중요한 접점이다.위 3 가지만 명심하면,적어도 연말에 너무 억울해할 일은 줄어들 것이다.화려한 말발이나, 수려한 미사여구는보고에서 중요한 부분은 아니라고 생각한다. 오히려, 자신감이 좌우한다.물론, 이 모든 것 위에는상사의 역량과 인간성이 우선하기 마련이지만,이것은 논외로 하기로 하자.그건 답이 없다는 걸 누구보다 잘 알지 않는가?
조회수 4900

안드로이드 앱의 Persistent data를 제대로 암호화해 보자! (2/2)

들어가기1부에서는, KeyStore 를 사용해 Shared Preferences 를 암호화 하는 법에 대해 알아봤습니다. 그리고 이 글에서는 Room을 사용한 Database 를 암호화 하는 방법에 대해 설명합니다.2018년 현재, 안드로이드 자체에서 데이터베이스를 암호화하는 기능을 제공해 주진 않습니다. 따라서 오픈 소스 프로젝트인 SQLCipher, SafeRoom 의 사용법 위주로 설명할 예정입니다. 또한 KeyStore 에 대칭키를 생성하는 기능은 API Level 23 이후에서만 가능하며, SQLCipher 가 Android KeyStore 를 지원하지 않고 있습니다.이로 인해 1부에서 소개한 키 암호화 메커니즘으로 보호한 별도의 키를 디스크 어딘가에 저장해 두고, 필요할 때만 복호화 해서 쓴 다음 복호화된 내용을 지우는 방식으로 구현해야 합니다. 하지만 이런 방식으로 사용하는 키는 메모리에 순간적으로 남기 때문에 좋은 공격 표면(Attack surface) 이 됩니다. 그 이유도 함께 다뤄 보겠습니다.SqlCipher team 에서 하루라도 빨리 현재의 char[] 형식의 passphrase 를 입력받는 대신, JCA 를 사용해 암호화하는 데이터베이스를 구현하길 기대해 봅시다.SqlCipher1부에서 보여드렸다시피 internal storage 에 저장한 데이터는 결코 안전하지 않습니다. 파일 DB 인 Sqlite 데이터는 포맷을 모르면 어차피 볼 수 없을테니 조금 다르지 않을까요? 그렇지 않다는 것을 다음 예에서 보여드리겠습니다. 루팅한 디바이스에서 adb pull명령으로 sqlite3 데이터베이스를 추출 후 내용을 열어보면 다음과 같습니다.$ hexdump -vC secure_database.sqlite3 00000000  53 51 4c 69 74 65 20 66  6f 72 6d 61 74 20 33 00  |SQLite format 3.| 00000010  10 00 02 02 00 40 20 20  00 00 00 02 00 00 00 04  |.....@  ........| 00000020  00 00 00 00 00 00 00 00  00 00 00 04 00 00 00 04  |................| 00000030  00 00 00 00 00 00 00 04  00 00 00 01 00 00 00 00  |................| 00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................| 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 02  |................| 00000060  00 2e 01 5a 0d 0f 95 00  02 0e a9 00 0e a9 0f c9  |...Z............| 00000070  0e 6f 0e 6f 00 00 00 00  00 00 00 00 00 00 00 00  |.o.o............| ... 00000d30  00 00 00 00 00 82 37 03  07 17 57 57 01 83 4d 74  |......7...WW..Mt| 00000d40  61 62 6c 65 73 71 6c 69  74 65 62 72 6f 77 73 65  |ablesqlitebrowse| 00000d50  72 5f 72 65 6e 61 6d 65  5f 63 6f 6c 75 6d 6e 5f  |r_rename_column_| 00000d60  6e 65 77 5f 74 61 62 6c  65 73 71 6c 69 74 65 62  |new_tablesqliteb| 00000d70  72 6f 77 73 65 72 5f 72  65 6e 61 6d 65 5f 63 6f  |rowser_rename_co| 00000d80  6c 75 6d 6e 5f 6e 65 77  5f 74 61 62 6c 65 05 43  |lumn_new_table.C| 00000d90  52 45 41 54 45 20 54 41  42 4c 45 20 60 73 71 6c  |REATE TABLE `sql| 00000da0  69 74 65 62 72 6f 77 73  65 72 5f 72 65 6e 61 6d  |itebrowser_renam| 00000db0  65 5f 63 6f 6c 75 6d 6e  5f 6e 65 77 5f 74 61 62  |e_column_new_tab| 00000dc0  6c 65 60 20 00 00 00 00  00 00 00 00 00 00 00 09  |le` ............| ... [리스트 1] Internal storage 에 저장된 SQLite3 database 를 dump 한 결과.역시 기대했던대로 데이터가 하나도 암호화되어 있지 않은 것을 확인할 수 있습니다. 그렇다면 가장 간단한 방법은 SQLiteDatabase클래스를 확장하는 일일 텐데요, 문제는 이 클래스가 final 로 상속 불가능하게 되어 있단 점입니다. 이 때문에 암호화된 SQLiteDatabase 구현체는 이 클래스 및 이 클래스에 강하게 결합되어 있는 SQLiteOpenHelper 를 온전히 쓸 수 없다는 문제가 있습니다. 즉, 바닥부터 새로 만들어야 하는 상황인데요, 다행히도 Zetetic 사에서 만든 SQLCipher for Android 는 이 문제를 모두 해결해 주는 고마운 오픈 소스 프로젝트입니다.SqlCipher 의 사용법은 기존의 SQLiteDatabase 에 의존하던 로직들의 import namespace 만 바꿔주면 되도록 구현되어 있어 마이그레이션 비용도 거의 들지 않습니다.// 안드로이드에서 제공해 주는 SQLiteDatabase 클래스명 import android.database.sqlite.SQLiteDatabase; // SqlCipher 에서 제공해 주는 SQLiteDatabase 클래스명 import net.sqlcipher.database.SQLiteDatabase; // 프로그램 시작시 native library 를 로드해줘야 한다. class MyApplication extends android.app.Application {    @Override public void onCreate() {        super.onCreate();        net.sqlcipher.database.SQLiteDatabase.loadLibs(this);    } } [리스트 2] android SQLiteDatabase 에서 SqlCipher SQLiteDatabase 로 마이그레이션 하기물론 두 클래스는 전혀 타입 호환되지 않지만, net.sqlcipher.database.SQLiteDatabase 의 모든 메소드 및 field의 signature 가 기본 android.database.sqlite.SQLiteDatabase 와 같기 때문에 이런 변경이 가능합니다. SqlCipher 개발팀의 수고에 박수를 보냅니다.RoomRoom 은 SQL 을 객체로 매핑해 주는 도구입니다. Room 을 이용해 데이터베이스를 열 때는 보통 아래와 같은 코드를 사용합니다.object Singletons {    val db: DataSource by lazy {        Room.databaseBuilder(appContext, DataSource::class.java, "secure_database")            .build()    } } abstract class DataSource: RoomDatabase() {    abstract fun userProfileDao(): UserProfileDao } // 클라이언트 코드에서 아래와 같이 호출 val userProfile: UserProfile = Singletons.db.userProfileDao().findUserByUid(userId) [리스트 3] Room database 의 정의 및 활용Sqlite 의 기본 동작은 파일 데이터베이스에 단순 Read 및 Write 만 합니다. 따라서 데이터베이스 접근시 암호화/복호화 동작을 하는 callback 을 주입해야 데이터베이스를 암호화 할 수 있습니다. 그리고 RoomDatabase.Builder 클래스는 데이터베이스를 열때 우리가 주입한 일을 할 수 있는 hook method(openHelperFactory) 를 제공해 주고 있습니다. 다음 코드를 살펴봅시다.class RoomDatabase.Builder {    class Builder {        /**        * Sets the database factory. If not set, it defaults to {@link FrameworkSQLiteOpenHelperFactory}.        */        @NonNull        public Builder openHelperFactory(@Nullable SupportSQLiteOpenHelper.Factory factory)    } } interface SupportSQLiteOpenHelper {    /**     * Create and/or open a database that will be used for reading and writing.     */    SupportSQLiteDatabase getWritableDatabase();    /**     * Create and/or open a database. This will be the same object returned by {@link #getWritableDatabase}.     */    SupportSQLiteDatabase getReadableDatabase();    /**     * Factory class to create instances of {@link SupportSQLiteOpenHelper} using {@link Configuration}.     */    interface Factory {        /**         * Creates an instance of {@link SupportSQLiteOpenHelper} using the given configuration.         */        SupportSQLiteOpenHelper create(Configuration configuration);    } } [리스트 4] Room builder 의 SupportSQLiteOpenHelper 주입 메소드 및 SupportSQLiteOpenHelper.Factory 인터페이스 정의설명을 최대한 간소하게 하기 위해 관심가질 필요 없는 코드 및 코멘트는 모두 제외했습니다. 아무튼 SupportSQLiteOpenHelper 구현체를 주입하면 뭔가 데이터베이스 작업 이전에 우리의 로직을 실행할 수 있을 것 같습니다.사실 이 인터페이스의 핵심은 바로 getWritableDatabase(), getReadableDatabase() 구현입니다. javadoc 에도 있지만 두 메소드로 반환하는 인스턴스는 같아야 하며 또한 암호화를 지원해야 한다는 것을 알 수 있습니다.결국 우리 목표는 Room 과 데이터베이스 암호화 로직을 연결해 주는 SupportSQLiteDatabase 구현체를 만드는 것임을 알 수 있습니다. 이 인터페이스는 규모가 제법 크기 때문에 이게 만만한 일이 아님을 직감하실 수 있을 겁니다.saferoom 도입으로 SupportSQLiteDatabase 인터페이스 구현체 사용하기앞서 살펴봤듯 SupportSQLiteDatabase 구현에는 상당한 노력이 필요하단 것을 알 수 있습니다. 그런데 고맙게도 saferoom 이라는 오픈 소스 프로젝트가 우리의 귀찮음을 잘 해결해 주고 있습니다. saferoom 의 SupportSQLiteOpenHelper 구현체를 간단히 살펴보면 아래와 같습니다./** * SupportSQLiteOpenHelper.Factory implementation, for use with Room  * and similar libraries, that supports SQLCipher for Android.  */ public class SafeHelperFactory implements SupportSQLiteOpenHelper.Factory {    private final char[] passphrase;    public SafeHelperFactory(final char[] passphrase) {        this.passphrase = passphrase;    }    @Override    public SupportSQLiteOpenHelper create(final SupportSQLiteOpenHelper.Configuration configuration) {        return(new com.commonsware.cwac.saferoom.Helper(configuration.context,            configuration.name, configuration.version, configuration.callback,            this.passphrase));    }    /**     * NOTE: this implementation zeros out the passphrase after opening the database     */    @Override    public SupportSQLiteDatabase getWritableDatabase() {        SupportSQLiteDatabase result = delegate.getWritableSupportDatabase(passphrase);        for (int i = 0; i < passphrase>            passphrase[i] = (char) 0;        }        return(result);    }    /**     * NOTE: this implementation delegates to getWritableDatabase(), to ensure that we only need the passphrase once     */    @Override    public SupportSQLiteDatabase getReadableDatabase() {        return getWritableDatabase();    } } /**  * SupportSQLiteOpenHelper implementation that works with SQLCipher for Android  */ class Helper implements SupportSQLiteOpenHelper {    final OpenHelper delegate;    Helper(Context context, String name, int version, SupportSQLiteOpenHelper.Callback callback, char[] passphrase) {        net.sqlcipher.database.SQLiteDatabase.loadLibs(context);        this.delegate = createDelegate(context, name, version, callback);        this.passphrase = passphrase;    }    abstract static class OpenHelper extends net.sqlcipher.database.SQLiteOpenHelper {        SupportSQLiteDatabase getWritableSupportDatabase(char[] passphrase) {            SQLiteDatabase db = super.getWritableDatabase(passphrase); return getWrappedDb(db);        }    } } [리스트 5] Saferoom 의 SupportSQLiteOpenHelper 구현체.소스 코드를 보면 SQLiteDatabase 의 원래 요구사항을 만족하지 못하는 구현 부분도 보입니다만, 그래도 이 정도면 수고를 꽤 크게 덜 수 있어 훌륭합니다.그리고 로직을 잘 보면 데이터베이스를 연 직후 암호로 넘겨준 char[] 배열을 초기화 하는 코드가 있다는 점입니다. 이것이 바로 이 문서의 서두에서 말했던 attack surface 를 최소화 하기 위한 구현입니다. 이 글의 주제에서 벗어난 내용이기에 여기서는 다루지 않습니다만, 궁금하신 분들은 부록 1: in-memory attack 맛보기에서 확인하실 수 있습니다.SqlCipher + SafeRoom + Room 구현 및 코드 설명이상으로 데이터베이스 암호화 전략에 대해 살펴봤습니다. 이 장에서는 실제로 연동하는 방법에 대해 다룹니다.불행히도 2018년 현재 SqlCipher 는 Android KeyStore 를 지원하지 않고 있습니다. 그리고 인스턴스 생성에 쓸 비밀번호로 CharArray 가 필요한데, 이 값은 한번 정해지면 불변해야 합니다. 여기 사용할 키를 KeyStore 에 저장하면 문제를 깔끔하게 해결할 수 있을 것 같습니다. 하지만 1부에서 살펴봤듯이 하드웨어로 구현된 Android KeyStore 밖으로는 키가 절대로 노출되지 않는다고 합니다. 이 문제를 어떻게 해결해야 할까요?먼저, SqlCipher 에 사용하기 위해 KeyStore 로 생성한 AES256 키의 내용을 한번 살펴봅시다.val secretKey = with(KeyGenerator.getInstance("AES", "AndroidKeyStore"), {    init(KeyGenParameterSpec.Builder(alias,             KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)        .setKeySize(256)        .setBlockModes(KeyProperties.BLOCK_MODE_CBC)        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)        .build())    generateKey() }) val keyInfo = with(KeyFactory.getInstance(privKey.getAlgorithm(), "AndroidKeyStore"), {    factory.getKeySpec(privKey, KeyInfo::class.java) }) println("Key algorithm : " + secretKey.algorithm) println("Key format : " + secretKey.format) println("Encoded key size: " + secretKey.encoded?.size) println("Hardware-backed : " + keyInfo.isInsideSecureHardware) // 실행 결과 Key algorithm : AES Key format : null Encoded key size: null Hardware-backed : true [리스트 6] AndroidKeyStore 에 저장한 Key 는 어플리케이션에서 직접 쓸 수 없다.저희가 보유중인 개발 시료 Nexus 5 에서 실행한 결과 위와 같이 나타났습니다. secretKey.encoded 의 값이 메모리에 있다면 이 값을 SqlCipher 생성자에 넘겨줄 수 있겠지만 값이 null 이네요. 보안 측면에서는 다행일 지 모르지만 우리 구현에서는 쓸 수 없으니 문제입니다. 그래서 별 수 없이 임의로 키를 만들고(AndroidAesHelper#generateRandomKey()), 1부에서 소개했던 AndroidRsaCipherHelper 를 이용해 암호화한 값을 Shared Preferences에 저장하는 식으로 구현해 봅시다.val settingsPrefs = appContext.getSharedPreferences("app_settings", Context.MODE_PRIVATE) val settings = SecureSharedPreferences.of(settingsPrefs) val dbPass = with(settings, {    /*     * String.toCharArray() 같은 함수를 쓰면 로직이 좀더 간단해지지만, JVM 에서의 String은     * Immutable 하기 때문에 GC 이전에는 지울 방법이 없으므로 attack surface 가 더 오랫동안     * 노출되는 부작용이 있다. 따라서 key의 plaintext 는 가급적 String 형태로 저장하면 안된다.     */    var savedDbPass = getString("DB_PASSPHRASE", "")    if (savedDbPass.isEmpty()) {        // KeyStore 에 저장해도 SqlCipher 가 써먹질 못하니 그냥 1회용 키 생성 용도로만 활용한다.        val secretKey = AndroidAesCipherHelper.generateRandomKey(256)        // String 생성자 사용: 이 문자열은 heap 에 저장된다.        savedDbPass = String(Base64.encode(secretKey, Base64.DEFAULT))        putString("DB_PASSPHRASE",  AndroidRsaCipherHelper.encrypt(savedDbPass))        // 메모리 내에 plaintext 형태로 존재하는 attack surface 를 소멸시켜 준다.        secretKey.fill(0, 0, secretKey.size - 1)    } else {        // decrypt 메소드 내부에서 String 생성자 사용하므로 base64 인코딩된 plaintext 키는 heap 에 저장된다.        savedDbPass = AndroidRsaCipherHelper.decrypt(savedDbPass)    }    val dbPassBytes = Base64.decode(savedDbPass, Base64.DEFAULT)    /*     * SqlCipher 내부에서는 이 char[] 배열이 UTF-8 인코딩이라고 가정하고 있다.     * 그리고 UTF-8 인코딩에서는 byte range 의 char 는 1 바이트니까,     * 아래 변환을 거치더라도 키 길이는 32 byte(256 bit)가 유지된다.     *     * UTF-8 인코딩에서는 32 글자 != 32 바이트가 아님에 항상 유의해야 한다!     */    CharArray(dbPassBytes.size, { i -> dbPassBytes[i].toChar() }) }) [리스트 7] 암호화한 SqlCipher 용 passphrase 를 사용하는 방법.위 코드를 사용해 char[] 타입의 값 dbPass 를 얻을 수 있습니다. 리스트 7을 이용해 얻은 dbPass를 아래 코드에 사용하면 SqlCipher - SafeRoom - Room 의 연동이 끝납니다.val dataSource = Room.databaseBuilder(_instance, DataSource::class.java, "secure_database") .openHelperFactory(SafeHelperFactory(dbPass))                .build() // 메모리 내에 plaintext 형태로 존재하는 attack surface 를 소멸시켜 준다. dbPass.fill('0', 0, dbPass.size - 1) [리스트 8] SqlCipher - SafeRoom - Room 연동하기위 코드에서 볼 수 있듯, 임의로 저장한 키를 Base64 인코딩으로 변환, 그리고 그것을 다시 CharArray 로 변환하는 과정에서 key 가 메모리에 존재해야 하는 순간이 있습니다. 이 구간을 바로 공격 표면(attack surface) 이라고 합니다.JVM 단에서 넘겨주는 Passphrase 를 SqlCipher 내부에서 native 로 어떻게 처리하고 있는지는 SqlCipher SQLiteDatabase 구현및 SqlCipher crypto 구현 에서 확인할 수 있습니다.결과 확인하기SafeHelperFactory 를 주입한 Room database 파일을 추출 후 hexdump 로 확인해 보겠습니다.hwan@ubuntu:~$ hexdump -vC secure_database.sqlite3 00000000  8c 0d 04 07 03 02 11 eb  a4 18 33 4f 93 e8 ed d2  |..........3O....| 00000010  e9 01 21 d7 49 df 25 9a  f4 1d c7 1e ff 2d b0 13  |..!.I.%......-..| 00000020  fc 17 9b 4b b2 1c a3 1d  7d 1d 69 76 b1 ea ec e8  |...K....}.iv....| 00000030  1f 50 e4 c4 6c 50 e6 82  58 27 b9 fe 85 21 27 99  |.P..lP..X'...!'.| 00000040  ec 54 53 ba 32 c6 59 09  b4 30 65 39 a0 75 3e c4  |.TS.2.Y..0e9.u>.| 00000050  b8 f7 ea 47 14 df c4 f0  7c be 9f 62 26 49 1c b2  |...G....|..b&I..| 00000060  0f 63 00 7a 09 7e 33 e0  43 2b eb ea 80 21 bb 5d  |.c.z.~3.C+...!.]| 00000070  5c 04 ff 57 a3 a3 7f c2  19 42 b9 67 6c e3 d5 c8  |\..W.....B.gl...| ... 00000d30  c1 f3 93 1f 4e 5b 6a 70  39 c2 e9 2c 3e 8f 7e ff  |....N[jp9..,>.~.| 00000d40  73 3a 9a 39 0d 8a 1a 3e  6b d4 5b de 1f 6d c4 b8  |s:.9...>k.[..m..| 00000d50  fb 62 3e 21 09 0a 31 20  37 5d 8d 0a 39 6d 35 31  |.b>!..1 7]..9m51| 00000d60  26 d6 b0 22 41 7e 6c 54  7d 77 22 ba 1b f3 cf 5a  |&.."A~lT}w"....Z| 00000d70  e5 47 97 76 f0 89 e5 98  b3 37 3c 8d 43 af 0e b9  |.G.v.....7<.C...| 00000d80  18 74 fd f5 2a 41 d8 b1  d9 70 32 0b 5c 93 4b 0d  |.t..*A...p2.\.K.| 00000d90  bc 60 4c 25 9a ec 53 23  90 60 b2 52 a8 a1 b1 87  |.`L%..S#.`.R....| 00000da0  f3 3e 03 3e ac 0a 75 a0  61 d8 bd 07 b8 5a 48 66  |.>.>..u.a....ZHf| 00000db0  57 85 13 ac 04 26 55 30  34 46 57 bf 8b 42 c6 2d  |W....&U04FW..B.-| 00000dc0  9e 82 a2 df 77 bb b3 2e  96 43 70 23 23 03 df 1d  |....w....Cp##...| ... [리스트 9] Internal storage 에 저장된 SQLite3 database 를 dump 한 결과. 리스트 1과 비교해 보자.이로서 오픈 소스의 힘을 빌려 우리 앱의 데이터베이스를 비교적 간편하게 암호화 할 수 있음을 알 수 있습니다.맺으며이로서 Persistent data 암호화에 대한 설명을 마칩니다. Android KeyStore 가 API Level 23 이상의 기기에서만 100% 동작한다는 점은 2018년 현재까지는 큰 단점입니다. 하지만 사소한 데이터라 하더라도 보안의 중요성은 날로 강조되고 있습니다. 따라서 빠르던 늦던 고객 데이터 암호화에 투자해야 할 순간이 다가온다는 점은 변하지 않습니다.언젠가는 적용해야 할 고객 데이터 보호의 순간에, 이 글이 여러분의 앱의 보안에 조금이나마 도움이 된다면 좋겠습니다.부록 1: in-memory attack 맛보기앞서 계속 반복해서 설명드렸던 메모리 내의 attack surface 를 찾아내는 방법을 간단히 설명해 보겠습니다. 잘 지키려면 잘 공격하는 법을 알아야 하므로 알아두면 좋지 않을까요? 그리고 일반적인 앱 개발과는 다소 동떨어진 이 장의 내용이 이해되지 않으신다면 한줄요약한 메모리 내부의 값도 때로는 안전하지 않을 수 있다 는 한마디만 기억해 두시면 됩니다. 모든 데모는 LG Nexus 5(Hammerhead), 시스템 버전 6.0.1(M) 에서 실행한 결과며 시스템마다 약간의 차이는 있을 수 있습니다.마켓에 출시한 앱들은 debuggable:false 가 설정된 상태이므로 힙 덤프를 바로 뜰 수는 없습니다. 그런데 어떻게 in-memory attack 이 가능할까요? 다음 리스트는 디버그 불가능한 앱의 힙 덤프를 시도할 때 보안 정책 위반 오류가 발생함을 보여줍니다.hwan@ubuntu:~$ adb shell ps | grep "com.securecompany.secureapp" USER PID PPID VSIZE RSS WCHAN PC NAME u0_a431   25755 208   1700384 100888 sys_epoll_ 00000000 S   com.securecompany.secureapp hwan@ubuntu:~$ adb shell am dumpheap 25755 "/data/local/tmp/com.securecompany.secureapp.heap" java.lang.SecurityException: Process not debuggable: ProcessRecord{b6f96fc 25755:com.securecompany.secureapp/u0_a431}     at android.os.Parcel.readException(Parcel.java:1620)     at android.os.Parcel.readException(Parcel.java:1573)     at android.app.ActivityManagerProxy.dumpHeap(ActivityManagerNative.java:4922)     at com.android.commands.am.Am.runDumpHeap(Am.java:1248)     at com.android.commands.am.Am.onRun(Am.java:377)     at com.android.internal.os.BaseCommand.run(BaseCommand.java:47)     at com.android.commands.am.Am.main(Am.java:100)     at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)     at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:251) [리스트 10] debuggable=false 설정된 앱의 힙 덤프 시도시 발생하는 예외(SecurityException)SuperUser 는 가능할까요? SuperUser 권한으로 앱을 강제로 디버그 가능한 상태로 시작해 보도록 하겠습니다.hwan@ubuntu:~$ adb shell 32|shell@hammerhead:/ $ su 1|root@hammerhead:/ \# am start -D -n "com.securecompany.secureapp/MainActivity" && exit Starting: Intent { cmp=com.securecompany.secureapp/MainActivity } hwan@ubuntu:~$ \# adb shell ps | grep "com.securecompany.secureapp" USER PID PPID VSIZE RSS WCHAN PC NAME u0_a431   27482 211   1700384 100888 sys_epoll_ 00000000 S   com.securecompany.secureapp hwan@ubuntu:~$ adb forward tcp:12345 jdwp:27482 hwan@ubuntu:~$ netstat -an | grep 12345                                                           tcp4       0      0  127.0.0.1.12345         *.*                    LISTEN     hwan@ubuntu:~$ jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=12345 java.net.SocketException: Connection reset     at java.net.SocketInputStream.read(SocketInputStream.java:210)     at java.net.SocketInputStream.read(SocketInputStream.java:141)     at com.sun.tools.jdi.SocketTransportService.handshake(SocketTransportService.java:130)     at com.sun.tools.jdi.SocketTransportService.attach(SocketTransportService.java:232)     at com.sun.tools.jdi.GenericAttachingConnector.attach(GenericAttachingConnector.java:116)     at com.sun.tools.jdi.SocketAttachingConnector.attach(SocketAttachingConnector.java:90)     at com.sun.tools.example.debug.tty.VMConnection.attachTarget(VMConnection.java:519)     at com.sun.tools.example.debug.tty.VMConnection.open(VMConnection.java:328)     at com.sun.tools.example.debug.tty.Env.init(Env.java:63)     at com.sun.tools.example.debug.tty.TTY.main(TTY.java:1082) Fatal error:  Unable to attach to target VM. [리스트 12] SuperUser 권한으로도도 Java 디버거를 붙일 수 없다.다행히도 debuggable=false 로 릴리즈한 앱은 자바 디버거(jdb)를 붙일 수 없으니 프로그램 실행을 매우 정밀하게 제어할 수는 없다는 것을 알 수 있습니다(debuggable=true 설정된 앱에 위 과정을 실행하면 어떤 일이 벌어지는지 직접 확인해 보세요!).하지만 안드로이드의 앱은 ‘linux process’ 에서 실행되므로 SuperUser 권한으로 process 메모리 전체 dump를 뜨는 것은 막을 수 없습니다. 정공법으로는 /proc/PID/maps 의 내용을 분석하면 됩니다만 제가 안드로이드를 깊게 알고 있는 것은 아니라, 어느 영역이 dalvik heap 인지를 알아낼 수 없었습니다. 이 때문에 프로세스 메모리를 통째로 떠서 내용을 헤집어보는 방식으로 공격해 보겠습니다. 여담입니다만, 데모를 위해 공격한 앱은 dumpsys 명령으로 확인해보니 약 6MiB 의 Java heap 을 쓰고 있는데요, 이 크기를 줄이면 줄일 수록 공격이 더욱 수월할 겁니다.아래 데모에서는 안드로이드 기기용(arm-linux-gnueabi)으로 컴파일한 gdb 를 미리 설치한 결과를 보여드리고 있습니다. 참고로 여기 보이는 [heap] 은 아쉽지만 native heap 이므로 우리 공격 목표는 아닙니다.1|root@hammerhead:/ \# cd /proc/27482 1|root@hammerhead:/proc/27482 \# cat maps 12c00000-12e07000 rw-p 00000000 00:04 8519       /dev/ashmem/dalvik-main space (deleted) ... b7712000-b771f000 rw-p 00000000 00:00 0 [heap] bee86000-beea7000 rw-p 00000000 00:00 0 [stack] ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors] 1|root@hammerhead:/proc/27482 \# ifconfig wlan0     Link encap:Ethernet          inet addr:192.168.12.117          inet6 addr: fe80::8e3a:e3ff:fe5f:64c9/64 1|root@hammerhead:/proc/27482 \# gdbserver –attach :12345 27482 Attached; pid = 27482 Listening on port 12345 [리스트 13] SuperUser 권한으로 gdbserver 실행.hwan@ubuntu:~$ adb forward tcp:23456 tcp:12345 hwan@ubuntu:~$ netstat -an | grep 23456 tcp4       0      0  127.0.0.1.23456         *.*                    LISTEN     [리스트 14] 로컬 포트 23456 으로 원격 포트 12345 를 연결하는 과정.이제 모든 준비가 끝났습니다. 개발 기기에서 gdb로 원격 프로세스에 접근한 뒤, 메모리를 덤프해 봅시다.hwan@ubuntu:~$ ./gdb (gdb) target remote 192.168.12.117:12345 Remote debugging using 192.168.12.117:12345 0xb6f92834 in ?? () (gdb) dump memory /tmp/com.securecompany.secureapp.heap 0x12c00000 0xb771f000 (gdb) [리스트 15] gdb 로 메모리를 덤프하는 과정.덤프한 힙 덤프 파일 속에 있을지도 모르는 문자열을 검색해 봅시다. 그 전에 잠시, 데이터베이스에 사용할 키를 어떻게 처리했었나 되새겨 볼까요? if (savedDbPass.isEmpty()) {        // ...        // String 생성자 사용: 이 문자열은 heap 에 저장된다.        savedDbPass = String(Base64.encode(secretKey, Base64.DEFAULT))    } else {        // decrypt 메소드 내부에서 String 생성자 사용하므로 base64 인코딩된 plaintext 키는 heap 에 저장된다.        savedDbPass = AndroidRsaCipherHelper.decrypt(savedDbPass)    } [리스트 16] Base64 인코딩을 처리하기 위한 임시 String 생성 과정.우리 로직은 256 비트의 키를 Base64 변환해서 디스크에 저장합니다. 그리고 256비트의 byte array 를 base64 변환한 결과는 (4 * (256 / 3)) / 8 = 42.66 바이트 -> 4의 배수여야 하므로 44바이트입니다. 약 1.34 바이트의 pad 를 맞추기 위해 문자열의 끝에 =가 최소 1글자 이상은 있을 겁니다. 한번 찾아봅시다.hwan@ubuntu:~$ strings /tmp/com.securecompany.secureapp.heap ... /masterkey ... user_0/.masterkey em_s 1337 ... [리스트 17] strings 명령을 사용한 힙 덤프 파일내의 문자열 검색의외로 = 나 == 로 끝나는 문자열이 발견되지 않습니다. 하지만 안심하기는 이릅니다. 이건 단순히 (공격자의 입장에서) 운이 나빠서 발견되지 않은 것일 뿐입니다. 우리가 원하는 어떤 ‘순간’ 에 힙 덤프 명령을 내리지 않았기 때문에 그렇습니다. 우리의 구현은 attack surface 를 매우 짧은 시간동안만 메모리에 노출하기 때문에 이 순간이 짧으면 짧을 수록, 디바이스의 성능이 좋으면 좋을 수록 순간을 잡아내기가 더욱 어려워집니다. 즉, 이 문서에서 보여드린 방식으로 CharArray 의 내용을 아주 짧은 시간 동안만 사용하고 지워버리면 내용을 탈취하기 굉장히 어렵습니다. 하지만 안심하기는 이릅니다. nano-time 단위로 앱을 실행할 수 있는 환경을 가진 국가급 공격자는 여전히 있기 때문입니다.그리고 이 방법은 루팅하지 않은 기기에서는 절대 재현이 불가능하므로 루팅되지 않은 환경일 경우에만 실행 가능하도록 한다던가 하는 방식까지 더한다면 공격자가 더욱 우리 앱을 뚫기 힘들 겁니다.여담입니다만 독자 여러분들 중 GameGuardian 처럼 다른 게임의 메모리값을 마구 바꾸는 앱이 어떻게 동작하나 궁금하신 분들도 있을 겁니다. 그런 류의 앱들도 바로, 이 장에서 설명했던 방식으로 동작합니다.장황했던 이 장의 내용을 한줄로 요약하면 Android KeyStore 로 보호하지 않은 키는 많은 수고를 들이면 뚫을 수 있다고 할 수 있습니다.부록 2: SQLite database 의 UPDATE / DELETE 구현 특성SQLite3 의 구현특성상, UPDATE / DELETE 시에 이전 레코드의 값이 남아있는 경우가 있습니다. 암호화 했으니 좀더 안전하다곤 하지만 찌거기 값을 굳이 남겨둬서 공격자에게 더 많은 힌트를 제공할 필요도 없습니다.이 문서는 암호화 구현에만 초점을 맞췄기 때문에 상세하게 다루진 않습니다만, LINE Tech blog에 소개된 True delete 는 이 문제를 해결하기 위한 방법을 제시하고 있으므로 그 문서도 한번 읽어보시길 권합니다.더 보기SQLCipherSafeRoomAndroid SQLite3 True delete - by LINE tech blogDifference between java.util.Random and java.security.SecureRandomAttack surface on security measuresAOSP: DebuggingRootbeer: Simple to use root checking Android library#하이퍼커넥트 #개발 #개발자 #안드로이드 #앱개발 #모바일 #PersistentData #인사이트 #개발후기
조회수 1279

Humans of TODAIT : CTO 유병한을 만나다

어느 화창했던 3월, ‘Humans of TODAIT’ 의 첫 주인공인 투데잇 CTO 유병한을 만나봤습니다. 투데잇 핵심엔진인 그의 이야길 함께 들어볼까요?Q. 자기소개 부탁드려요.안녕하세요! 투데잇에서 CTO를 맡고 있는 유병한입니다. ‘SW 마에스트로’라는 과정에서 대표님과 좋은 인연이 되어 투데잇의 전신인 투데잇브레이커부터 지금까지 열심히 개발중입니다. 안드로이드분야에 관심이 많아서 시작하게 되었는데, 차츰 기술 스펙을 확장해 나가면서 서버개발부터 최근엔 iOS 개발까지 맡고 있습니다.Q. ‘꿈을 향한 오늘, 투데잇’ 이라는 슬로건처럼 CTO님의 꿈에 대해 들을 수 있을까요?제겐 두가지 꿈이 있는데요, 먼저 투데잇이라는 서비스 자체에 대해선 전국민 앱으로 거듭나고 싶어요. 기존 교육관련 산업에서 우뚝 솟을 수 있는 서비스가 되고 싶은데요, 공부하는 사람의 입장에서 처음부터 끝까지 도움을 줄 수 있는 서비스를 제공하고 싶어요. 그래서 ‘저 사람이 일하는 투데잇은 일하기 좋은 회사다!’ 라던가 ‘성장하기 좋은 회사!’라는 인식을 주고 싶어요! 여러사람들을 심쿵!하게 만들고 싶습니다(웃음)음.. 그리고 제 개인적인 꿈으론 진짜 언젠간 해보고 싶은건데, 다큐멘터리 내셔널 지오그래픽급의 사진작가가 되고 싶어요.이래저래 심쿵하는 사람이 되고, 심쿵하게 만드는 서비스를 제공하고 싶습니다.Q. ‘개발자’로서의 시작은 어땠나요?음 제가 처음부터 개발자가 되고 싶단 생각은 안했어요. ‘개발’과 인연의 끈이라고 되짚어본다면, 아마 어릴 적에 접했던 나모웹에디터로 홈페이지 만들기?였던 것 같아요.그리고 수시를 과감하게 버리고 제가 즐겁게 할 수 있었던 컴공이나 관련 학과로 찾다보니 지금의 과에 입학하게 되었어요. 대학교와서도 다양한 학교 수업 중 개발 관련 수업을 맛보면서 ‘아 이게 나한테 맞겠구나!’ 싶어서 본격적으로 공부했어요.(Q. 앗, 그럼 ‘개발자’라는건 갑작스러운 전환이었나요? )그렇다고 해서 아주 갑작스럽진 않았어요. 학생때 사진찍는걸 즐겨서 색감에 대한 거라던가 화면에서의 구도에 대한 이해같은게 높았거든요. 학과에서 배웠던 다양한 편집툴들이 지금의 UI 센스에 발판이 되지않았나 생각해요.어느 순간 하나가 쓸데없던 적은 없었던 것 같아요. 자그마한 순간순간들이 지금의 저로 만든 것 같아요.Q. 본인이 맡은 업무에 대해 어떻게 접근하나요?일단 맡은 분야에 대해서도 그렇고 제가 욕심이 좀 많아요(웃음) 내가 잘하고 싶은 욕심, 가지고 싶은 욕심이 여러 힘든 과정을 이겨낸 원동력이 아닐까 생각해요.Q. 예를 들면 어떤 경험이 있나요? 조금 더 자세하게 듣고 싶어요!음.. 아 이건 좀 비밀인데 중학교때 사진을 찍게 되었는데 보통 그냥 똑딱이카메라쓰던 시절이었거든요. 그런데 DSLR이 너무 가지고 싶어서 조르기도하고 한푼두푼 모으기도해서 결국엔 DSLR을 손에 넣었어요. 되게 사소한것 같지만 나름 원하는걸 얻어낸 뜻깊은 추억이죠. 뭐든 전문가처럼 해야겠단 욕심이 강한 것 같아요.(Q. 오.. 그런데 책상에 책이 되게 많이 쌓여있네요? )책을 쌓아두는게 사실 좀 최근 관심이 가진 프로그래밍 언어라던지 관심이 가는거 위주로 가져다 놓긴 했어요. 아이폰 관련 서적이 몇개 있는데, (이제 세달 정도) 레퍼런스로 많이 찾아보기 위해서 책들이 상시대기하고 있어요. 2–3권. 스위프트라는 언어를 배우면서 기존의 코틀린 자바 스크립트 등 다양한 관심이 생겨서 , 언어들에 대한 욕구가 좀 큰 요즘입니다~(웃음)Q. 일을 하다보면 힘든 순간도 많았을 것 같아요.힘든 순간은 매순간인것 같아요(하하) 그래도 진짜 엄청 힘든 순간이 있었는데 제겐 ‘아버지’가 되게 큰 힘이 되주셨어요. 아버지께서 목사님이시거든요. 평생 부산에서만 사셨던 분이 산골 깊숙히 들어와서 농촌교회를 준비하시면서 힘든 부분이 분명 클텐데도, 지금은 사회복지기관까지 운영하시는 걸 보면 정말 대단하신 것같아요. 홀로 타지에서 모든걸 감내하셨던 부분이, 그리고 하고자 하는 일에 대한 끊임없는 열정을 많이 배워요.그런 아버지를 보고 감명받아서인지 저도 모르는 사이에 창업가 마인드가 생겼던 것 같아요. “ ‘내’가 능동적으로, ‘내’회사를, ‘내’회사에서, ‘내’회사를 위해 일하는, ‘내’일을 한다. ”라는 생각 자체가 아버지의 영향을 많이 받지 않았나싶네요.음 그리고 역시나 빼놓을 수 없는! 우리 투데잇을 사랑해주시는 유저분들이 정말 큰 힘이되요. 실은 투데잇브레이커 당시에 제가 이일을 시작하게 된 이유자체가 ‘이 서비스를 사용하는 유저들이 있다’란 거였거든요. 제겐 그분들이 제 모든 이유인것 같아요. 제 스스로가 성장할 수 있는 이유, 투데잇이 인정받을 수 있는 이유, 그 모든 이유의 근간이라고 생각해요.Q. 항상 좋은 리뷰만 있진 않았을 것 같아요. 혹시 가장 기억에 남는 리뷰 있나요?되게 오래전 리뷰인데, 많이 부족했던 투데잇을 보고 ‘대체 언제쯤 기능 업데이트 되냐, 3D게임 만드냐’라고 하셨던 리뷰가 가장 기억에 남아요. 순간은 되게 기분이 상하면서도 점점 더 잘해내고 싶단 의지가 생기더라구요. ‘내가 3D게임 개발하는 것도 아닌데, 이정도라니. 더 개발 좀 열심히 해야겠다.’하구요. 지금은 그분께 감사하죠.그때 당시만해도 투데잇이란 서비스가 되게 부족했을텐데도 끊임없이 애정해주시면서 기다려주신 유저분 중 한 분이니까요.좋았던 리뷰들은 정말로 셀 수 없이 많아요. 저희가 매주 리뷰를 함께 공유하는 자리가 있는데, ‘성적이 올랐다는 리뷰’부터, ‘투데잇 덕분에 공부 스타일이 또는 생활 습관이 바뀌었어요’, ‘지금 수험생활을 하고 있는데 힘을 얻고 있어요.’ 그리고 ‘합격소식’까지. 진짜 큰 힘이 되죠. 제가 어떤 무형의 무언가를 하고 있단게 현실에서 드러난다는게. 그게 정말 큰 힘이 되요.Q. 나에게 ‘기술’ 이란?저는 아까 말씀드렸던 것처럼 욕심이 좀 많은 편인데, 그 중 제일 욕심 많은게 바로 ‘기술’이예요. 이 분야에서 전문가가 되기 위해서 하나하나 하면 하나를 깊게 파는 스타일이예요.그래서 다양한 분야의 기술을 심도있게 잘하고 싶은 욕심이 있었는데, 투데잇을 개발하면서 그런 욕심을 부릴 수 있는 기회가 주어졌어요. 당시만 해도 당장 없는 개발팀원 자릴 메우기 위해 열심히 욕심부렸던게 지금의 이 자리에 앉게되지 않았나 생각됩니다.제게 기술이란건, 기술 자체가 가지고 싶은 그 무언가예요. 끊임없이 욕심을 내서 계속해서 닿고싶은 그런 존재?(웃음)Q. 오픈소스활동에서 핫하다는 이야기가 있는데요~오픈소스 활동이 아주 거창하진 않아요. 아직은 걸음마단계 수준이죠. 음 처음엔 제가 필요한 오픈 소스를 사용하면서 발견한 에러나 버그 부분에 대해 피드백을 드렸어요. 되게 간단한 부분이었는데 그쪽에서 긍정적으로 받아들여주셨어요. 그런 상호 피드백이 오가면서 관심을 갖게되었습니다.제트브레인스라는 회사에서 근무하시는 분의 피드백을 받는 과정이 오픈 소스활동을 통해 서로 교류할 수 있단 점이 매력적이었어요.활동을 하게된 결정적인 계기는, 다른 회사에 다니는 친구와 함께 깃허브에 올리고 IOS 개발 커뮤니티에 올렸더니 반응이 핫하더라구요. 큰 이슈는 아니지만, 사람들이 긍정적인 피드백을 해줘서 즐거운 순간이었죠.앞으로도 작은것부터 하나씩 해나갈 예정이에요. 큰 규모의 기술은 아니더라도, 투데잇의 ANDROID/IOS에 필요한, 하지만 불편함을 해소해줄 수 있는 라이브러리나 툴들을 만들어나가려고 생각을 하고 있구요. 기존에 투데잇 안에서만 쓰던 걸, 조금씩 정리해서 공유해나갈 생각입니다.Q. 안드로이드 앱 개발을 하면서 소프트웨어 아키텍쳐에 관한 고민이 있다던데, 어떤 고민을 하고계신지 들어볼 수 있을까요?지금 저희 나름대로, 기존에 있던 MVVM, VIPER라든지 그런 아키텍쳐들을 많이 보고 차용을 해서, 투데잇에도 적용을 해나가고 있는 중이에요. 직접 해보니 학교에서 책으로 배운 “프로그래밍 구조가~” 나 “아키텍쳐 구조가~” 에 대해 필요성을 뼈저리게! 몸소 부딪혀가면서 느끼는 중입니다.개발을 하다보면, 사소한 버그나 문제점을 많이 발견하게 되는데, 이를 어떻게 미리 테스트할지 또 어떻게 검증할지에 대해 고민을 하고 있어요. 그런데 기존 소스코드는 각각 다른 기능을 하는 코드가 한데 뭉쳐있어서, 이걸 분리해서 테스트하기에 용이한 아키텍쳐에 대해 개선 및 적용해나가고 있습니다.또 안드로이드와 아이폰 버전을 개발 중인데, 각각 플랫폼에 종속적인 부분을 빼놓고 두 버전 모두 동일한 구조를 가지고 갈 수없을까에 대한 고민 중인데요, 이러한 고민을 함께 하실 분들이 오셨으면 좋겠어요. 이상적인 구조를 향해서 말이죠. (웃음)Q. 현재 일하고 있는 팀원이 7명이나 된다고!네! 대표님과 단둘이 끌어왔던게 엊그제같은데, 벌써 7명의 투데잇팀으로 구성되었네요. (웃음) 사실 제게 투데잇팀은 그냥 공기같은 존재예요. 같이 있을 땐 중요성을 모르다가도, 누구하나 자릴 비우게 되면 그 느낌이 진짜 오묘해요. 서로가 서로를 너무 당연하게 자리하고 있다고 느끼고 있어서요.언젠가 한번 기호형님(COO)이 자릴 비운적이 있었어요. 아직 일한지 1년도 채 안되는데도, 옛날 옛적부터 알고 있던 사람처럼 그때 그 공허감이 되게 크더라구요.사실 좋을 땐 다 좋죠. 중요한건 일하면서 분명 좋지 않은 순간이 올텐데, 이때 서로 어떻게 커뮤니케이션을 하느냐 인것 같아요. 부정적 피드백에 대해 받아들이는 자세가 우리 팀의 가장 메리트라고 생각해요.지금 팀원들은 일에 있어서 피드백이 오갈 땐, 감정적인 건 잠깐 내려놓고, 객관적으로 앞으로 더 발전 방향에 있어서 뭘 어떻게 해야할지를 생각하는 태도를 보이거든요. 이 부분에 대해 ‘서로 핏이 잘 맞는다’라는 문장이 딱 맞는 표현 같아요.그래서 전 “같이 있으면서 어색하지 않은 그런 사이”가 좋아요. 물론 시간이 지나면서 서로 맞춰가는거겠지만, 왜 그런거 있잖아요. 함께 일을 해도 계속해서 어색한 사람이 있고 조금 풀리는 사람이 있는거. 그런 점이 저희 팀의 메리트라고 생각합니다.Q. CTO의 입장에서, 같이 일하고 싶은 개발자는 어떤 사람인가요?당연한 거겠지만, 일단 서로 존중해줄 수 있는 사람이면 좋겠어요. 아무리 비즈니스라지만 서로가 서로에게 예의와 매너가 갖춰진 사람을 원합니다.업무적으론, 뭔가 새롭게 배우는거에 대해 두려움이 없는게 좋은 개발자의 기본 자세라고 생각해요. 그래서 바로바로 과감한 도전 정신이 있는 사람! 그리고 제게 없는 재능을 가져서 서로가 상호 보완해나갈 수 있는 파트너면 좋겠어요.Q. 지원하고 싶은, 지원을 생각하는, 이 글을 보고있는 사람에게 한마디 부탁드려요!“만약에 지원을 하신다면, 마음을 단단히 먹어야할겁니다.”초반만 하더라도, 스타트업에 대한 환상이 있었어요. 이상적인 모습, 장밋빛 회사생활만을 꿈꿨거든요. 언론에서 소위 말하는 ‘젊은 창업가!’의 그 이면엔, 장밋빛을 현실화 하기위해 매일매일이 고난의 길이란걸 잊지 않으셨음 해요. 스타트업이란게 자신의 한계를 확인하는 과정이기 때문에, 같은 출발선에서 함께 발전하기 위해 달릴 준비가 되신 분을 환영합니다.또 자기 나름의 미션이 있고, 그걸 회사의 가치 성장에 일치시켜 나가면서 함께 실현해나가실 분을 모십니다!우리의 이상을 위해 함께 이 현실을 헤쳐나가실 분을 찾습니다!#투데잇 #팀원소개 #팀원인터뷰 #팀원자랑 #기업문화 #조직문화 #개발자 #개발팀 #CTO
조회수 1075

컨택센터 행복 플래너들의 리더, 김은경 센터장을 만나다

대명레저산업 직원 인터뷰 <대명인사이트>의 일곱 번째 주인공입니다. 대명리조트를 이용하는 모든 고객님들이 한번쯤은 이용해보셨을 그 곳, 회원서비스팀 컨택센터의 김은경 센터장을 만났습니다. 밝은 미소로 친절히 대답해주시는 김은경 센터장과 함께 다양한 이야기를 나누었습니다.Q1. 간단한 자기소개 부탁드려요.여행을 준비할 때의 설렘과 여행을 즐기면서 느끼는 행복 그리고 다녀온 후의 소중한 추억을 드리기 위해 일하는 행복 플래너들의 리더, 김은경 센터장입니다.Q2. 대명리조트 컨택센터를 소개해주세요.대명리조트 여행을 계획하시는 모든 고객님들이 가장 먼저 만남을 시도하는 곳이 저희 컨택센터인 것 같아요. 현재 상담업무와 지원업무를 하는 100여명의 직원으로 운영 되고 있고, 전국 16개의 사업장에 대한 예약과 부대시설 등의 전반적인 이용안내가 주 업무에요. 특히 오션월드, 스키월드, 개화시기 등 사계절에 걸맞는 만족스러운 여행을 설계하는데 도움을 드리고 있습니다.Q3. 기억에 남는 에피소드 있나요?정말 많은 에피소드가 있었지만, 그 중에서도 가장 기억에 남는 일이 있어요. 어느 고객분께서 계속해서 비발디파크 예약을 취소하고 다시 예약하기를 반복하며 하루에 몇 번씩 동일한 문의를 했던 적이 있었어요. 이런 상황이 계속 발생하다 보니 결국 그 분의 보호자님께 연락을 드려야 했는데.  알고 보니 심각한 언어 장애가 있는 친구더라구요. 비발디파크를 너무 좋아하지만 가지 못하는 상황이고 또 말을 배우기 위해  계속해서 누군가와 이야기를 해야 했는데 그럴 상대가 주변에 없다 보니 저희 컨택센터에 전화 통화를 하면서 대화를 시도했던 같아요. 그런 사정을 알고 난 뒤부터, 저희 상담사들도 그 친구와  전화연결이 되면 차근차근 대화하면서 소통을 이어갔고, 나중엔 말이 많이 늘었다며 기뻐했던 기억이 나네요.Q4. 아쉬웠던 상담 문의는 무엇이었나요?가장 큰 아쉬움은 고객분들의 문의에 대해서 확실한 해결책을 드리지 못하는 경우죠. 예를 들면, 사업장 입 출입구 혹은 사업장 내에서 폭우나 폭설 때문에 교통체증이 발생하는 경우가 있거든요. 사업장 직원들도 사실 최소 인원을 제외하고는 모두 밖으로 나가서 고생하고 있는 걸 저희가 아는 상황에서 자연적인 문제를 해결해드리기는 어려운 상황이었어요. 무조건 사업장으로 전화 연결을 해달라 혹은 시간적 보상을 해달라 하는 고객분께 죄송하다라는 말 밖에는 할 수 없는 상황이어서 이런 상담이 가장 힘든 것 같습니다.Q5. 상담사를 꿈꾸는 분들께 조언해주세요.나만의 여행 경험을 공유하면서 여행의 질을 높여주고, 그로 인해 고객님께 행복과 만족을 충족시켜 드릴 수 있는 사람은 대명 컨택센터 상담사 뿐이라고 확신합니다. 단순 상담사가 아닌 행복전도사가 되고 싶은 분들이라면 꼭 함께 했으면 좋겠습니다.Q6. 마지막으로 한 말씀 해주세요.이번 기회를 통해 컨택센터를 적극적으로 응원하고 지원해주시는 저희 회원서비스팀과 유관부서 외 모든 임직원분들, 그리고 고생하는 상담사들에게 ‘수고한다’, ‘감사하다’ 격려해주시는 고객님들, 모두 감사합니다. 그리고 바쁜 회사 생활 속에서 한 가정의 아내와 엄마로서 소홀했던 저를 언제나 이해해주고 곁에서 힘이 돼주는 소중한 가족에게 사랑한다고 전하고 싶어요. 마지막으로 저를 믿고 최선을 다해 함께 뛰어주는 컨택센터 여러분께 작게나마 버팀목이 될 수 있는 멋진 리더가 되어주고 싶습니다. 진심으로 감사드립니다. 인터뷰 내내 행복 바이러스를 전달해 준 김은경 센터장. 대명레저산업을 이용하는 모든 고객님들이 즐겁고 행복한 여행을 할 수 있도록 노력하는 컨택센터의 노고를 엿볼 수 있는 시간이었습니다. 대명인사이트는 다음 주인공과 함께 다시 찾아오도록 하겠습니다.#대명 #대명리조트 #대명레저산업 #CS #컨택센터 #센터장 #고객만족 #고객중심 #행복전도사 #직무정보 #구성원인터뷰 #공채정보 #채용정보 #대명채용
조회수 1382

짝사랑도 끝내야 할 때가 있는 법

"안녕하세요. 몇 년간 좋아하는 오빠에게 몇 번을 고백했습니다. 그리고 이번 4월 무렵 다시 한번 좋아한다고 고백을 했습니다. 하지만 제가 들은 대답이 뭔지 아세요? "다음 기회에!"라고 하더군요. 사랑 고백이 뽑기도 아니고, 다음 기회라니요.. 사랑에 있어 희망고문은 아니란 말이 떠올랐어요. 저처럼 이렇게 고백에 대한 황당한 대답을 들어본 사람이 또 있을까요?"-  스푼 유저 '꽃처럼' 님의 사연중A. 안녕하세요. 스푼 라디오입니다.'꽃처럼'님의 사연을 받고 사실 얼마나 당황스러웠는지 모르겠습니다. 얼마나 큰 용기를 가지고 고백을 하셨을지에 대한 짐작도 되었고, 무례한 대답을 듣고 얼마나 속상하셨을지도 느껴졌습니다. 몇 년간 좋아하는 사람에게 몇 번이고 진심을 담아 고백을 하셨다니 정말 많이 좋아하셨을 거라고 생각합니다.그리고 지금의 마음은 어떠하신지도 궁금하기도 합니다. 무례하기 짝이 없는 황당한 대한 들었어도, 좋아하는 마음은 쉽게 내 뜻대로 되지 않으니까요. 단지, 말씀하신 것처럼 사랑은 뽑기도 아니고, 사랑은 희망고문도 아니라는 걸 스스로에게 다시 한번 말해보는 건 어떨까 라는 생각을 해봅니다.내가 좋아하는 사람이 나를 반드시 좋아해 줄 필요도, 할 수도 없는 일임을 잘 아실 거라고 생각합니다. 그래서 짝사랑이 힘든 게 아닐까 싶습니다. 하지만, '꽃처럼'님의 진실된 마음을 가볍게 여기는 상대방에게 그 사랑과 시간은 너무 과분 하단 생각이 듭니다. 나를 사랑해주지 않아서가 아니라, 나의 대한 진심을 짓밟은 몹쓸 말을 하는 그런 사람에게 더 마음을 주기 너무나도 나의 마음이 너무 소중하다는 걸 알아주셨으면 좋겠습니다.나의 대한 가치와 존중을 알아봐 주는 사람에게 나의 마음을 쏟으셨으면 좋겠습니다. 물론 머리는 이해해도 마음이 따라주지 않아서 짝사랑이지만요. 적어도 나에 대한 진심에 모욕감을 주는 사람은 마음속에서 하루빨리 떠나보내 주는 게 전 좋은 선택일 것 같다고 감히 적어봅니다. 나도 분명 예전에 누군가를 짝사랑해 본 적이 있었던 것 같다. 지금은 까마득히 기억도 안 나긴 하지만 말이다.누군가를 좋아하고 사랑한다는 것은 세상에서 가장 아름답고도 아픈 일이 아닐까 싶다. 함께 하는 사랑은 행복한 순간도 있지만 이별을 겪을 수 있기에 가슴 아픈 상처가 될 수도 있고, 혼자 사랑하는 사랑은 혼자만의 감정을 추스르느라 어려운 일임이 분명하다.사람 마음이라는 게 참 분명 내 마음인데 왜 내 뜻대로 되지 않는 걸까? 아무리 그 이성적인 사람이라도 사랑 앞에선 마음 앞에선 결국 약자가 되어버린다. 짝사랑을 해 본 사람들은 아마 공감하지 않을까?1. 언제부터 왜 어떻게 이 사람을 좋아하게 됐는지 나도 잘 모르겠다.2. 그냥 이유 없이 어느 순간부터 나의 모든 시선, 마음 그리고 소중한 이 사람에게 향해있다.3. 그저 바라반 봐도 좋다가도 나도 모르게 은근슬쩍 이 사람으로부터 사랑받고 싶단 생각을 한다.짝사랑은 무조건적으로 마음 아프거나 새드 앤딩으로 끝나진 않지만, 가끔은 나 스스로를 위해서 새드 앤딩이 되어야 할 때가 있다. 사랑은 다른 사랑으로 잊힌다는 말이 있다. 신기하게도 다른 누군가를 좋아하게 되거나 연애를 하게 되면 시간이 흘러 전 사람이 잊히곤 한다. 마치 아무런 일이 없었던 것처럼. (물론 계속 기억에 남는 사람도 있다) 하지만 짝사랑도, 연애도 하면서 중요한 게 딱 한 가지 있다는 걸 최근 돼서야 정확히 아주 명확히 알게 된 사실이 있다.정말 뻔하고도 클리쉐 한 말이지만,'내가 나를 먼저 사랑해야 남을 사랑할 수 있다'라는 말이다. 누구나 쉽게 할 수 있고 많이들 들어본 이야기일 거라고 생각한다. 특히나 사랑에 약한 사람들이 있다. 평상시에는 정말 똑 부러지던 사람이 '사랑'이란 두 글자에 세상 바보 천지가 되는 사람들이 있다. 그건 바보라서가 아니라 그만큼 열정적이게 사랑을 하는 타입의 사람이라고 생각한다. 하지만 분명한 건, 타인을 사랑하면서 나를 사랑하는 법을 잃어버리면 안 된다라는 말을 하고 싶다. 내가 나를 존중하고, 나를 아끼고 사랑할 때 정말 다른 누군가도 나를 존중하고 사랑하고 아껴준다는 말이 뭔지 몇 번의 연애를 끝으로 알게 되었다. 짝사랑에서도 마찬가지이다. 나를 사랑해주지 않는 사람을 사랑하지 말란 말이 아니다. 그저, 짝사랑에도 상도덕(?)이 있다는 것과 고백에 대한 거절, 나의 진심에 대한 존중은 받아야 된다고 생각한다.누구에게나 사연은 있다.당신의 사연, 고민을 함께 나누는 공간 스푼 라디오입니다.사연에 채택되신 스푼 유저 '꽃처럼'님께 스푼 라디오 공식 굿즈를 선물로 보내드립니다.여러분의 이야기를 듣고 싶습니다. 스푼 라디오에 사연을 보내주세요.사연에 채택되신 분들께 소정의 선물을 보내드립니다.자세한 사항은 [email protected]으로 문의 바랍니다.
조회수 1760

실패와 좌절에 대하여

2013년 경병현 이사와 함께 집을 나와 상경한 지 어느새 4년.그동안 올린 블로그 글들과, 페이스북에 올린 글, 일기들을 돌이켜봤다.나와 타인에 대한 원망, 두려움, 슬픔으로 점철된 과거를 돌이켜보며 많은 생각이 들었다.요즈음, 나에게 "실패하면 어떻게 할 것이냐?"라는 질문을 하는 사람들이 많다.제대로 답변을 못할 때도 많고, 그 질문 자체에 공격적으로 반응한 적도 없지 않은 것 같다.사람들이 나에게 묻는 이 질문에 대해 차분하게 고민해볼 시간을 가졌다.실패라는 것은 무엇인가? 행복이란 것은 무엇인가?실패와 행복에는 공통점이 있다. 모두 나에 의해 자의적으로 쓰인 단어라는 것이다. 실패와 성공이라는 것의 잣대는 엄격해지기도 하고, 때로는 더 낙관적으로 변하기도 한다.고등학교 시절에는 용돈을 많이 받아 매점에서 빵을 자유롭게 사 먹는 친구들이 부러웠다.대학교 시절에는 군대를 이미 다녀온 사람들이 부러웠다.군인 시절에는 그냥 민간인만 보면 다 부러웠다.창업 초기, 하루에 빅맥 하나로 연명할 때는 밥만 안 굶고 사면 행복할 것 같았다.내가 생각하는 행복의 기준은 계속해서 변화했고, 실패에 대한 내 시선은 매우 엄격해져 왔다.매점에서 빵 사 먹을 수 있는 친구들이 부러웠던 17살의 나와 마주해본다. 창업 초기의 24살의 나와 마주해본다. ‘17살의 나’와 ‘24살의 나’는 현재의 나를 부러워한다. 빵도 자유롭게 사 먹을 수 있고, 하루 세끼 꼬박 챙겨 먹으면서도 강한 열망을 가질 수 있는 일을 하고 있기 때문이다.그럼에도 불구하고, 나는 나 자신이 항상 부럽지 않았다. 내 주변엔 너무나도 능력 있는 사람들, 사업을 잘 해내고 있는 사람들이 많다. 그들을 보면 4년간의 시간이 하찮게 느껴지고 조금도 버티고 싶지가 않다. 500원짜리 빵 하나 사 먹는 것이 부러웠던 내가 수억 원의 자금으로 회사를 경영해왔음에도, 어떠한 행복도 느껴지지 않았고 실패와 좌절만이 나와 가까이에 있었다. 그것이 내가 지난 3년간 겪어온 커다란 오류 중 하나였다.나는 늘 내 사업을 비관적으로 봤다. 내 인생을 비관적으로 바라봤다. 그렇게 해야만 나를 채찍질하고 앞으로 나아가게 할 수 있다고 믿었다. 항상 '부족하다'를 되뇌었다. 내 일기장 어디를 둘러봐도 내 부족함에 대한 원망, 앞으로 에 대한 각오만이 가득할 뿐 현재에 대한 냉철한 시각은 찾아볼 수가 없다.일본에서 3대 경영의 신으로 불리는 이나모리 가즈오는 '교세라 철학'을 통해 사업의 마음가짐에 대해 이렇게 말했다.제대로 된 강렬한 소망을 품어보지 못한 사람은 목표를 세울 수도 이룰 수도 없다. 아무리 괴로운 상황에 처해도 자신의 인생과 회사의 장래를 비관적으로 보지 마라. 지금 아무리 괴롭고 고통스러울 지라도, '내 인생은 분명 장밋빛이다. 밝게 피어날 것이다'라고 믿어야 한다. 희망찬 미래와 목표에 대한 강한 소망과 열망을 모아 바위를 뚫을 듯한 기세로 싸워야 한다.이나모리 가즈오의 이러한 철학은 나에게 큰 깨달음을 줬다. 강한 소망과 열망을 모아 바위를 뚫을 듯한 기세로 싸웠을 지라도 패배했다면, 그건 그냥 그렇게 끝나기로 정해져 있기 때문이다. '실패하면 어떡하지'라는 고민으로 강한 소망과 열망이 비관과 두려움으로 바뀌면 바위를 뚫을 듯한 기세로 싸울 수도 없고, 패배할 확률을 도리어 높이는 꼴이 되어버리고 마는 것이다.1년 뒤, 나의 실패가 정해져 있는 운명이라 할 지라도, 지금의 열망과 소망에는 영향을 미칠 수 없다. 우리의 인생, 그리고 업은 실패와 같은 주관적이고 결과론적인 단어 따위에 쓰이고 영향을 받는 것이 아니다.어차피 죽을 인생, 왜 우리는 이렇게 고통스럽게 살아가는가? 어차피 한 줌의 재가 되어버릴 육신일 뿐인데 무엇이 우리의 소중한 현재를 고통으로 뒤덮을 수 있단 말인가?우리는 현재를 살아간다. 미래를 위해 살아간다는 것은 죽음, 인생의 끝과 훨씬 가까이 닿아있는 무언가를 위해 살아감을 뜻한다. 우리가 살아가고 있는 것은 현재이며, 현재는 과거의 찰나의 합이 이뤄진 지금 이 순간의 찰나다.지금 이 순간에도 우리는 생기를 잃어가고 죽음을 향해 나아가고 있다. 그럼에도 불구하고 우리는 이러한 것에 고통받거나 좌절하지 않는다. 죽음이란 것은 어차피 찾아오기 때문이다. 그것이 어떠한 형태로, 어떠한 시기에 찾아오든 우리는 그것을 담담하게 맞이해야 한다.실패라는 것도, 행복이라는 것도, 성공이라는 것도 이와 같다. 그것이 어떠한 형태로, 어떠한 시기에 찾아올지 우리는 알 수 없다. 나는 실패, 성공, 행복과 별개로 강한 소망과 열망으로 사업을 이끌어나갈 것이다.이 시점에서 내가 이해할 수 있고 예측할 수 있는 것은 아무것도 없다. 아직 존재하지도 않는 ‘불안’이라는 감정 따위가 지금의 나의 태도, 낙관적 마음가짐, 소망, 비전에 감히 영향을 끼칠 수 없어야 한다.우리는 우리 스스로 인생을 지배하고 있고 모든 것을 이해하고 있다고 생각한다.그것이 인간이라는 존재의 한계다.보이지 않는 것은 이해할 수 없고, 이해되지 않는 것은 볼 수 없는 것이 모든 생명체의 숙명이다.우리 안에 갇힌 돼지는 사료를 퍼먹으면서도 자신의 운명을 알지 못한다.부자연스러운 환경에 갇혀 살아가다 고통스럽게 죽어 인간에게 먹힐 것이라는 것을 알지 못한다.돼지가 볼 수 있는 시선은 거기까지 인 것이다. 돼지는 죽는 그 순간까지도 실패하고 좌절하지 않는다.'사육'이라는 인간의 간단한 개념을 돼지는 이해할 수 없기 때문이다. 그것이 돼지라는 존재의 한계다.우리 또한 우리가 보지 못하는 것에 의해, 이해할 수 없는 것에 의해 흘러가고 있다.그저 흐르는 것이다. 그저 흐르고 있는 우리를 얼어붙게 만드는 것이 두려움, 원망, 고통이다.이해할 수 없는 미래에 대한 비관적 마음가짐은 흐르고 있는 우리를 차가운 얼음으로 얼려버린다.그러나 중요한 것은 언젠가는 우리 모두 다시 흐른다는 것이다. 지금 느끼고 있는 두려움과 원망과 고통이 시간이 지나 아무것도 아닌 것이라는 걸 안 순간부터 우리는 다시 흐른다.그렇게 살아가는 것이 우리의 숙명인 것이다.두려워할 일도 없다.지금 이 순간 우리는 존재하고 있다.실패할 것도 없다.살아있다는 것 만으로 실패에 대한 개념은 언젠가 '추억', '경험'이라는 이름으로 다시 쓰여진다.3월의 봄이 찾아왔다.겨울잠에 든 동물이 일어나고, 식물들이 개화하는 봄이다.그러나 봄은 희망이 아니다. 겨울의 관성을 깨는 변화의 계절인 것이다.겨울잠에 든 동물이 일어나면 자연 생태계 속에서 죽어갈 확률이 크다.식물이 개화하면 짧은 시간에 또 다른 죽음을 맞이한다.그렇다고 해서 동물이 겨울잠을 평생 자지도, 식물이 개화를 멈추지도 않는다.고통과 죽음이 기다리고 있다 할 지라도 동물들은 깨어나고 식물들은 피어난다.동물도 식물도 이렇게 자연스럽게 흘러가는데, 우리는 억지로 이것을 바꿔보려 한다. 살면서 절대 실패하기 싫다면, 방법은 의외로 간단하다.지금 하고 있는 것을 포기하면 된다. 평생 겨울잠에서 깨어나지 않으면 된다. 고통도 시련도 모두 사라진다.이제 성공한 것인가?
조회수 1678

삼분의일 상표권 소회

올해 중순인가? 고객센터로 너희 브랜드 반값인 상품도 검색되는데 어떻게 된거냐는 문의가 들어오기 시작했습니다. 검색해보니 '모 업체'가 '삼분의일'을 본인 회사명 뒤에 붙이고 제품 설명 워딩도 저희와 똑같이 베껴서 혼란을 주고 있었습니다.아직 삼분의일이 상표등록이 되지 않은 시점이라 법적 대응을 할수 없었고, 대표님께 정중하게 전화드렸습니다. 그런데 뜻밖에 본인도 저번주에 삼분의일 상표등록을 신청했으니 법적으로 문제가 되지 않는다 라고 하셨습니다."어떤게 진짜 삼분의일 제품이냐"는 문의가 하루에도 수십통 왔는데 그때마다 마음이 찢어졌습니다. 우리의 노력이 어디론가 새어나간다는 찜찜함은 제품을 잘못 구매하시고 우리 고객센터로 전화하는 고객님이 수십명 될때쯤 참기 힘든 괴로움으로 바뀌었습니다.  짜증과 허탈함의 극치는 라디오에 저희가 소개되었을때였습니다.혹시나해서 '모 업체'의 스마트스토어에 가보니 주문폭주 감사 배너가 띄워져 있었습니다. 당연히 매스 트래픽은 가격이 저렴한 곳으로 흐를수 밖에.... 이외에도 맴찢하는 상황은 셀수없을 만큼 많았습니다. 특히 홈쇼핑 MD들도 헷갈려하는걸 목격했을때..마음속 깊이 곪아가는 고통을 느끼고 있었는데 오늘 '삼분의일' 상표등록이 완료되었습니다. 몸속에서 거대한 고름을 짜낸 기분입니다. '모 업체'는 발빠르게 '삼분의일 가격'이라는 키워드로 전환해서 여전히 저희에게 매달려 있습니다. 대단합니다.상표등록이 가능하도록 도움을 주신 주재연 변리사님께 큰절을 하고 싶습니다. 상표등록을 미루고 계신분은 지금 당장 시작하세요. 연락주시면 최고의 변리사님 소개시켜드릴게요.'모 업체' 덕분에 상표권에 대해서 흠뻑 배웠습니다. 훌훌 털어내고 다시 정진하도록 하겠습니다. 삼분의일 화이팅#삼분의일 #매트리스 #베개 #제품기업 #상표권분쟁 #문제해결 #인사이트 #경험공유
조회수 1546

원하는 정보를 5초 안에 인지할 수 있게 하자

우리나라에서 웹 서비스가 아이디어에서 출발해 출시되기까지 여러 단계를 거치게 되는데 크게는 기획, 디자인, 개발의 3단계를 거치게 된다고 볼 수 있다. 각 단계별로 세분화된 역할들이 있어도 결국은 각각 기획자, 디자이너, 개발자로 분류된다. 어니스트펀드에서는 그들이 제품개발팀을 이루고 있다.어니스트펀드 제품개발팀나는 그중 개발자로 속하고 퍼블리싱 & 프론트 개발을 하고 있다. 퍼블리싱은 디자이너가 그린 디자인된 화면을 웹페이지용 프로그래밍 언어라고 할 수 있는 HTML과 CSS로 웹 문서화하는 것이고, 프론트 개발은 HTML과 CSS로 만들어진 웹문서를 사용자의 의도/목적에 따라 기능이 동작하도록(주로 데이터 입출력, 예를 들자면 네이버 검색창의 자동 완성이나, 네이버 메인의 다음 뉴스 보기 등) 기능을 개발하는 것이다.어니스트펀드에서는 팀원들이 자신의 지식/경험을 공유하는 브런치 글을 돌아가면서 쓰고 있고 나도 함께하기로 결정하였다. 내가 가치 있게 공유할 수 있는 내용이 무엇인지를 고민하면서 나의 과거 경험들을 생각해보았다.나는 2002년 웹 디자인을 시작으로 퍼블리싱 업무를 겸하다 2004년부터 퍼블리싱 업무를 본격적으로 했고 2011년부터 스타트업에 합류하면서 기획 및 프론트 개발까지 제품 개발에 있어서 서버 개발을 제외한 사용자와 접하는 모든 업무를 두루 경험하였다. 보통 디자인 전공자들은 기획파트로 전업하는 경우가 많지만 나는 프로그래밍 언어로 코드를 작성하는 것이 재미있어 기회가 닿을 때마다 업무 영역을 넓혀왔다.따라서 기획과 디자인, 퍼블리싱, 프론트 개발에 이르는 사용자와 접점이 많은 다양한 업무를 해오면서 경험한 것을 바탕으로, 서비스를 구성하고 화면을 개발하는 데 있어 도움이 되는 유용한 내용을 공유하고자 한다.1. 많을 땐 나눠서 해결하자정보가 많다는 것은 정리 정돈할 물건이 많다는 것과 비슷하게 생각할 수 있다. 물건이 목적에 맞게 정리되지 않으면 찾기 어렵고 정리해놓더라도 쉽게 어질러질 수 있다. 정보도 마찬가지로 목적에 맞게 정리가 안되어 있을 때 이해가 어렵게 되고, 이해가 어려워서 이해를 돕기 위한 불필요한 설명이 덧붙여지다보면 더욱 이해하기 어려운 결과를 낳게 된다. 그렇게 되면 결국 설명하는 말만 늘어나고 고객의 이해는 저편에 남게 된다.웹페이지가 뜨는데 1초, 훑어보는데 3초, 원하는 정보를 캐치하는데 5초로 충분해야 한다. 사용자가 원하는 정보를 5초 안에 캐치하지 못할 정보의 양이라면 정보를 나누는 것이 좋다. 2. 제목을 생략하지 말자목적으로 나누어진 정보를 사용자가 빠르게 캐치할 수 있도록 돕는 가장 중요한 요소는 바로 제목이다. 제목은 본문을 다 읽지 않아도 내용을 어느 정도 짐작할 수 있게 한다. 따라서 훒어보는데 3초라는 의미는 한 페이지의 메뉴와 제목을 훑어보는데 필요한 시간이다. 이런 제목의 중요성 때문에 제목은 직관적이어야 하고 되도록 생략하지 말아야 한다. 생략을 할 때는 제목이 없어도 이해가 가능하며, 생략된 제목을 누구나 유추할 수 있을 경우가 아니면 제목의 생략을 피하도록 한다. 위 캡쳐화면은 네이버 메인 콘텐츠의 일부를 캡처한 이미지다. 네이버 메인 중 제목이 생략된 예는 왼쪽 하단 영역인 '주제형 캐스트'뿐이다. 다른 영역들은 '뉴스스탠드', '쇼핑' 등 제목을 생략하지 않고 노출시키고 있다. 메인 페이지처럼 목적이 다양한 페이지일수록 콘텐츠의 성격을 분명히 알 수 있게 하는 제목은 짧은 시간 안에 원하는 정보를 찾는데 도움을 준다.3. 한눈에 중요 정보를 읽을 수 있게 하자그다음으로는 정보의 배치이다. 해당 정보가 발생한 원인, 결과 등 고객이 인지하는 과정에 기반한 그룹으로 나누는 것이 좋다. 정보를 배치할 때는 개별 정보의 중요도 순서와 왼쪽에서 오른쪽, 위에서 아래로 흘러가는 흐름대로 배치고 중간에 역행하는 구성이 없는 것이 좋다. 국내 대형 인터넷 쇼핑몰의 상품 목록을 보면서 위 설명을 이해할 수 있다.정보 배치에 정답이 있는 것은 아니지만 마치 정답이 있는 것처럼 상품, 제목, 할인율, 가격, 현재 판매현황에 이르는 순서대로 나열하고 있다. 이는 선두업체를 따라 흉내 낸 것이 아니라 이와 같은 구성이 인지하기에 용이하기 때문에 모두 이와 같이 구성했다고 생각한다.   4. 어렵지 않게 보이도록 하자서비스에 대한 정보를 전달하고 나서 우리가 기대하는 바는 고객이 서비스를 이해하고 우리 서비스를 이용하게 하는 것이다. 쇼핑몰에서는 주문을 받는 것일 것이고, 어니스트펀드의 경우는 대출이나 투자를 신청하는 경우이다. 서비스를 이용하게 하려면 고객의 정보를 필수적으로 입력을 받아야 한다. 어니스트펀드의 경우는 대출 및 투자에 대한 금융서비스이기 때문에 더욱 많은 정보를 고객에게 요청한다. 고객의 정보를 웹 상에서 입력을 받을 때는 "폼"이라는 일종의 정형화된 웹페이지 구성항목을 이용하게 되는데 이것은 정형화되어있기 때문에 남들과는 다른 개성적인 방식을 이용하기는 어렵다. 금융서비스의 입력 폼이 아주 쉽지는 않다는 것을 고객들은 여러 다른 서비스를 이용하면서 어느 정도 알고 있다. 그러나 고객이 중간에 포기하지 않고 제대로 서비스 이용을 완료할 수 있도록 어렵지 않게 만들어야 하고, 언제나 경쟁사의 서비스를 확인하고 경쟁사보다는 어려워 보이지 않도록 만들어야 한다.5. 순서는 반드시 지키자순서는 여러 가지가 있다. 입력해야 할 항목이 무엇인지를 알려주는 입력항목 및 입력하는 창(=입력 필드), 입력하는데 필요한 도움말, 입력해야 할 항목들을 나열하고 전송/입력완료 버튼까지의 순서가 곧 정보의 순서이다. 이 중 쉽게 놓치는 부분은 첫 입력 필드에서 입력완료 버튼까지의 여정 중에 연관이 없는 링크나 버튼을 추가하는 경우이다. 이 순서는 디자인상으로는 잘 구분되지 않을 수 있지만, 웹코드 상으로는 100% 지켜져야 하는 순서이고 디자인과 웹코드의 순서가 일치하면 가장 좋은 결과이다.'다음'과 '네이버'의 로그인 영역을 비교해보자면 두 포탈 서비스 모두 메인 검색창에서 탭키로 아이디 입력 칸까지 이동할 수 있지만, 아이디 입력 후 비밀번호를 입력하고 로그인 버튼을 누르기까지의 탭키 이동 경로가 다르다. 다음 로그인 화면네이버 로그인 화면다   음 : 아이디 입력 -> 비밀번호 입력 -> 로그인 버튼 -> 로그인 상태 유지 순서로 이동한다.네이버 : 아이디 입력 -> 비밀번호 입력 -> 로그인 상태 유지 -> IP보안 선택여부 -> 로그인이다.탭키로 입력필드를 이동하는 경우가 곧 웹코드상에서의 각 입력 필드의 순서가 되는데, '다음'과 같은 경우는 아이디/비밀번호 입력 후 로그인에 대한 옵션을 키보드로 선택하기 위해서는 로그인 버튼을 지나쳐야 선택할 수 있다. 로그인에 대한 옵션은 로그인 버튼을 선택하기 전에 나오는 것이 더 자연스럽지 않을까? 눈에 보이는 순서도 중요하지만 각 입력필드의 논리적 우선순위를 지키는 것 또한 중요하다.6. 틀린 부분을 즉시 명확하게 알려주자고객이 언제나 우리가 기대한 값을 입력해주지는 않는다. 이 경우 너무너무 명확하게도 오류가 발생한 시점에 오류가 발생한 지점을 알려주는 것이 필요하다. 10개의 입력필드가 있는데 입력완료 버튼을 누르자마자 10개 항목 구구절절이 맞고 틀리고를 알려주는 것보다는, 오류가 발생한 시점에 알려주는 것이 훨씬 인지가 빠르다. 따라서 오류 항목을 보여주어야 하는 곳은 해당 입력필드의 다음이고 전송 버튼이나 후속 작업 이전이 되는 것이다. 위 캡쳐화면은 어니스트펀드에서 대출을 받고자 할 때 이름과 생년월일을 입력하는 부분이다. 필자는 생년월일 부분에 5월 32일이라고 없는 날짜 정보를 넣었고, 이와 같은 입력 실수는 사용자가 실수를 했다는 것을 시스템이 "정확한 정보를 입력해 주세요"라고 즉시 알려주고 있어 사용자가 입력을 실수하지 않도록 돕고 있다. 웹 페이지를 보는 고객들은 아무런 도움 없이 해당 서비스를 이해하고 이용할 수 있어야 한다. 똑같은 정보라고 하더라도 어떤 순서로 어떻게 보여주느냐에 따라서 인지와 인식은 크게 개선될 수 있다. 하물며 정보까지 가공을 하게 되면 더욱 큰 개선을 이끌어 낼 수 있다. 각자가 맡고 있는 서비스에서 5초 안에 고객이 원하는 정보를 웹 페이지 내에서 바로 인지할 수 있는지를 생각해보고 아니다면 테스트해보고 개선해보자.#어니스트펀드 #개발자 #개발팀 #UX개발 #철학 #인사이트
조회수 1485

영화관에서 ‘치킨’ 어때요? CJ CGV F&B사업팀 이홍철 님

코미디 영화 사상 1,500만 명 관객을 기록한 <극한직업>. 극장 밖을 나선 순간 치킨이 생각나는 건 어쩌면 당연한 일. 이에 발맞춰 CJ CGV에서 ‘BBQ 직화구이 치킨’을 선보였다. 영화관에서 치느님 영접을 가능케 주인공을 만나보았다.  유학파 출신 셰프가 극장으로 온 이유? ▲ ‘BBQ 직화구이 치킨’을 탄생시킨 CJ CGV F&B사업팀 이홍철 님지난 1월 24일, CJ CGV에서 야심 차게 출시한 ‘BBQ 직화구이 치킨’. 부드러운 순살 치킨에 바비큐 소스, 쫄깃한 떡꼬치를 더해 남녀노소의 입맛을 사로잡기 충분했다. 마성의 ‘BBQ 직화구이 치킨’을 탄생시킨 이는 CJ CGV F&B사업팀 이홍철 님이다. 이홍철 님은 프랑스요리학교 ‘르 꼬르동 블루(Le Cordon Bleu)’를 졸업한 유학파 출신으로 프랑스 엠배서더 호텔, 국내 웨스틴 조선호텔을 거쳐 지난 2010년, CJ CGV로 오게 됐다. 셰프로서 이름을 알릴 수 있는 유명 호텔을 마다하고, CJ CGV로 오게 된 이유가 궁금해졌다.음식과 엔터테인먼트가 결합하면 재미있겠다는 생각이 들었습니다. 그래서 CJ CGV로 오게 됐죠.CJ CGV F&B사업팀은 CJ CGV에서 판매하는 모든 식음 제품의 기획, 개발, 마케팅, 프로모션 등을 총괄하고 있다. 타 극장과 다른 점이라면? 차별화된 제품을 기획하고 개발하는 이홍철 님이 있다는 것! 해외를 비롯 대부분의 극장 사업자가 매점 메뉴를 수급 받아 판매하는 방식을 택한 것과 달리,  CJ CGV는 국내 유일 극장 매점 메뉴 개발자인 이홍철 님을 통해 자체적으로 맛 좋은 매점 먹거리를 만들고 있다. 그만큼 CJ CGV에서 그는 없어서는 안 될 중요한 존재인 셈. 그 동안 CJ CGV는 이홍철 님과 함께 다양한 메뉴 출시를 통해 극장에서 이색 먹거리를 접할 수 있도록 다변화를 꾀했다. 대표 제품으로는 지난 2012년, 프리미엄 팝콘 문화를 만든 고메 팝콘을 시작으로 죠스떡볶이와 콜라보해 튀김범벅과 라볶이를, 스쿨푸드와 손잡고 대표 메뉴인 ‘모짜렐라 스팸계란마리’를 냉동김밥 형태로 세계 최초로 출시했다.왜 이렇게 팝콘이 맛있어지는 건데?  ▲ 팝콘의 ‘맛’을 업그레이드하기 위해 불철주야 노력하고 있는 이홍철 님이색 먹거리의 첫 신호탄은 ‘팝콘’이다. 이홍철 님의 첫 완성작이라 말할 수 있는 팝콘의 시작은 팝콘의 ‘맛’ 업그레이드 연구였다. 그는 기존 팝콘보다 더 맛있는 제품을 만들기 위해 연구에 연구를 거듭했다. 시중에 판매하는 국내외 팝콘은 다 먹었고, 다양한 종류의 옥수수로 직접 팝콘을 튀겨보기도 했다. 이뿐만이 아니었다. 팝콘에 고소한 맛을 더하기 위해 기존에 사용했던 팜유 대비 원가가 30%나 비싼 코코넛 오일을 사용하기도 했다. 그 다음 스텝이라 할 수 있는 고메 팝콘 개발은 두 세배 노력이 더해졌다. 팝콘 표면에 치즈와 초콜릿 등 다양한 원재료를 사용했는데, 열이 가해지면서 그대로 녹아버렸던 것. 이홍철 님은 이를 보완하기 위해 온도와 습도를 함께 조절하는 쇼케이스까지 제작했단다. 밤낮없이 제품을 개발했지만, 곧바로 출시하진 못했다. 기존 팝콘보다 만들기도 어렵고, 원가도 비싸고, 취급도 까다롭다는 게 이유였다. 그렇다고 포기할 수는 없을 터. 많은 사람을 설득시키기 위해선 ‘맛’으로 승부를 볼 수밖에 없었다. 임원분들께서 자주 다니는 동선을 찾아 고메 팝콘을 올려놨어요. 하나씩 드셔보시라고요. 맛있다고 하시더니 한번 판매해보라고 기회를 주셨죠.▲ ‘뭘 좋아할지 몰라서 다 준비했어!’ CJ CGV에서만 만날 수 있는 고메 팝콘2012년 마침내 고메 팝콘이 출시됐고, 제대로 통했다. 프리미엄 팝콘을 취향에 따라 골라 먹을 수 있다는 점이 고객에게 매력적으로 다가온 것. 고메 팝콘을 먹기 위해 일부러 CJ CGV를 찾거나 배달해 먹는 고객도 있었다. CJ CGV에서는 ‘매점’ 대신 ‘팝콘 팩토리’라는 이름을 사용하였고, 국내 최초 새로운 팝콘 문화를 형성했다. ▲ 신제품 출시 전, 품평회는 필수!물론, 모든 팝콘이 성공했던 것만은 아니었다. 와사비 열풍이 불던 4년여전. 이홍철 님은 와사비 팝콘을 만들기로 했다. 와사비 향을 내는 원재료를 구하기 위해 가까이로는 아시아부터 멀리로는 유럽까지 샅샅이 찾아보았다. 와사비 팝콘에 대한 내부 평가는 매우 긍정적이었다. 마지막 고객 품평회 날. 이홍철 님은 와사비 팝콘의 초록색이 마치 푸른곰팡이 같다는 청천벽력 같은 이야기를 듣게 되었다. 결국 충격을 받고 출시를 접었다고. 망고 맛, 불닭 맛 등 7가지 시즈닝을 뿌려 먹는 쉐이크 팝콘을 판매했을 때. 기대했던 것보다 고객의 반응이 좋지 않아 판매를 접어야만 했다. 실패를 통해 그가 얻은 해답은? 바로 제품의 이름을 듣고 그 맛을 상상했을 때 ‘먹고 싶다’는 생각이 들어야 한다는 것. 거듭되는 실패에도 이홍철 님이 계속 도전할 수 있었던 건 CJ CGV의 아낌없는 지원이 뒷받침되었기 때문이란다. 극장에서 치느님을? 한국형 매점 메뉴는 현재 진행 중!CJ CGV와 이홍철 님의 도전은 팝콘에만 국한하지 않았다. 전 세계 극장 메뉴가 팝콘, 콜라, 핫도그 등 미국식 메뉴로만 구성되어 있다는 점을 주목했고, 국내 고객들이 선호할 수 있는 한국형 매점 메뉴를 개발하기로 했다. 한국인들이 좋아하는 음식을 ‘한국형 매점 메뉴’라 재정의하고 한국인들이 최애 메뉴인 치킨과 분식 등을 극장 환경에 맞게 개발했습니다.▲ 출시 후 뜨거운 인기를 끌고 있는 ‘BBQ 직화구이 치킨’ 이렇게 탄생한 게 바로 ‘BBQ 직화구이 치킨’이다. 아이디어는 좋지만, 현실화를 끌어내기까지 쉽지 않았다. 특히 치킨 특유의 냄새가 가장 큰 장애물. 취식을 보다 쉽게 하는 방법도 고려해야 했다. ‘맛’을 놓치고 싶지 않았던 그는 직화구이 치킨에 인공 훈연제를 첨가하는 대신 직접 불에 일일이 굽는 방법을 선택했다. 또한 순살 닭고기로만 구성하면 식감이 단조로울 수 있어 떡꼬치를 추가했다. 제품 기획부터 출시까지 14개월 동안 고생한 결과물이 나왔을 때 가장 보람찼다고 말한다. ’BBQ 직화구이 치킨 전국 15개 직영매장 중심으로 선 오픈 한 후 오는 3월 말 전국 직영 극장 중심으로 확대할 예정이다. 또한 CGV에서는 지역 상생의 일환으로 ‘춘천 닭갈비’도 판매 중이다. 앞으로 CGV에서는 비장의 한국형 메뉴를 매년 선보이겠다는데 벌써 내년 제품이 무엇이 될지 설레게 된다.   ▲ 이젠 CJ CGV에서 먹는 즐거움도 누려보세요!고객들이 더 만족할 수 있는 제품을 만들고자 고민하고 노력하겠습니다.매점 메뉴라는 고정관념을 깨고 꾸준하게 제품을 확장하고 있는 이홍철 님. 그가 만들어 낸 다양한 제품을 통해 보는 즐거움을 넘어 먹는 즐거움까지 만끽할 수 있었던 것이 아닐까. 앞으로 CJ CGV와 이홍철 님이 선보일 새로운 제품을 기대해본다.[채널 CJ] #CJ #CGV #BBQ직화구이치킨 #CGV고메팝콘 #CGV치킨 #영화관치킨 #구성원인터뷰 #직무소개 #직무정보 #F&B사업팀 #이홍철님 #기업문화 #CGV채용 #CGV공채
조회수 1184

스타트업에게 투자보다 더 중요한 것, 고객만족

상담을 진행하다 보면, 구매자(=고객 = 클라이언트, ※ 넷뱅 사이트에서는 판매자와 구매자로 회원이 나뉜다)님이 제게 털어놓는 고민은 업종을 떠나서 정말 다양합니다.여러 번 다른 곳에서 사기를 당해 극도로 조심스러워, 시작을 머뭇거려하는 케이스, 다 좋은데 작업 마감기한이 빠듯해서 빨리 좀 적합한 작업팀 좀 찾아달라는 케이스, 예산도 여유롭고, 시간도 충분한 데 본인이 원하는 기능이 꼭 되어야 한다고 고집하지만, 그건 상상으로만 가능한 일이라 결국에는 사업기획을 변경해야 하는 케이스, 세세하게는 모바일웹 페이지를 만들지 네이티브 앱을 만들지 고민하는 케이스, 정부 지원금을 받아 채용을 했는데, 오히려 그 지원금이 사장님의 족쇄가 되어 직원들이 말을 안 듣는다는 케이스, 온라인 홍보마케팅 비용으로 매월 몇백 이상을 태우지만 광고대행사가 제대로 일을 하는건지, 안하는건지 도통 모르겠다는 케이스 등등 다양한 케이스를 접하게 됩니다.  크게, 돈 문제, 사람 문제, 시간 문제, 퀄리티 문제 등으로 나뉠 수 있겠네요.이 분들의 고민을 해결해주는 것, 만족스런 작업물을 손 안에 쥘 수 있게 저희가 손 닿는 한, 도와드리는 것.설령 분쟁이 생겨 잘잘못 시비를 가릴 때 중재해서 양쪽 다 피 흘리는 것을 최대한 줄이는 것.바쁠 뿐더러 이 분야 전문지식이 없을 땐, 전속 담당자 배정을 요청해서 프로젝트가 끝날 때까지 케어 해드리는 것.우리 회사는 '해결과 고객만족'에 가치를 두고 있습니다.넷뱅이라는 회사를 창업하면서, 학교에서도 배우지 못한 것을 터득한 것이 있다면 그건, 기업을 성장시키는 근본은 잘 만들어진 서비스/제품도 아니요, 딱 들어맞는 사업모델도 아니요, 든든한 돈줄인 자금도 아니요, 영업력, 마케팅도 아니요, 호흡이 잘 맞는 팀원들도 아니요, 멋진 회사 이름도 아니요, 멋진 사무실은 더더욱 아니요, 바로 사용자(고객)이라는 것입니다.내 고객이 누군지 알고, 그들의 고민이 우리의 고민이 되고 그분들의 불편함을 해결해줄 수 있는 무언가를 제공해주는 것. 더 나아가, 기쁘게 하는 것이 무엇인지 늘 고민해야 합니다.초기 고객을 확보하고 흡족시켜서 재구매를 유도하는 것이 초기 사업의 성공에 아주 중요한 요인이 됩니다.  매출로 직결되기 때문이죠.  영화 라라랜드를 일곱 번 봤다는 사람도 있듯이, 재구매는 만족의 증거이고 입소문은 돈 안드는 진짜 바이럴 마케팅입니다.   고객을 기쁘게 하면, 고객이 영업사원 한 사람의 역량보다 더 나을 수도 있습니다.매일 한 명씩 감동한 한 명의 고객이 자기 지인에게 소개한다면, 한달 동안 신규고객은 얼마가 될까요.  배가 아니라 제곱이 됩니다.  고객만족도 X 회사 연차수는 회사 성장속도와 비례합니다.마케팅 업계 20년차 지인의 말에 의하면  사람들이 '아~ 나 들어본 것 같아'하는 정도가 되려면 적어도 마케팅 비용을 월 4천만원씩 3개월 간 질러야 한다고 합니다.  최소 1억이 필요하다는 말인데, 이 금액은 스타트업에겐 1년 매출액도 안되는 기업이 허다합니다.  오히려, 어설픈 페이스북 관리, 블로그 운영관리 보다는 마케팅 예산이 책정되어 있다면 그 돈을 고객관리에 쓰라고 하고 싶습니다.굳이 돈쓰며 휘발성 강한 온라인 마케팅 하지 않아도 되는, 고객이 한 번 체험해보고 '우와' 할 수 있게 하는 힘이 있는 우리 회사에게 있는가를 늘 자문해야 합니다.  마케팅과 영업이 중요하지 않다는 게 아니라, 순서가 달라져야 한다는 겁니다.  마케팅을 하기 전에, 내 고객들은 충분히 만족을 하고 있는지 알아야 합니다.  스타트업 대표에게, 펀딩, 씨드머니, 투자, 너무나 매력적인 단어입니다.  그러나, 고객이 내 서비스/제품을 알게된 순간 부터 후기를 달기까지의 모든 과정이 체계화, 시스템화 되지 않은 상태에서, 내 고객이 어디에 뿔나 있는지 모르면서 돈으로 마케팅을 지르는 건 밑 빠진 독에 물 붓기 아닐까요?#넷뱅 #스타트업 #스타트업창업 #고객가치 #고객중심 #고객사랑

기업문화 엿볼 때, 더팀스

로그인

/