모험가의 끄적노트/진행했던 프로젝트

바다거북스프를 주제로한 안드로이드 앱 만들어보기 -1-

DevChw 2022. 6. 22. 17:22

본 글은 혼잣말 위주로 작성되었습니다.

 

안드로이드 스튜디오를 이용하여 간단한 앱을 제작하고자 아이디어를 내기 시작했다. 생활에 편의를 가져다주는 어플이나 건강 정보를 알 수 있는 어플, 간단한 쇼핑 어플 등 여러 생각을 해보았지만 내가 개발에 대해 깨닫기 도전에 많은 어플들이 이미 숨 쉬면서 자리를 지키고 있는 중임을 깨달았다. 앱을 개발하는 데에 있어서 창의성은 중요한 부분이라 생각하는데, 시장에 이미 존재하는 앱이 존재하고 만약 그 앱이 터줏대감이라면 당연히 살아남기 힘들 것이고 자연스럽게 성공적인 앱 개발이 되지 않을 것이다. 즌슥, 시장에 존재하지 않으면서 또는 존재하더라도 차별화된 앱을 만들어 내는 것이 현시대 개발자들의 가장 큰 고민거리라고도 생각한다.

 

그렇게 고민하던 도중 친구들이나 가족끼리 즐겨하던 바다거북스프라는 게임을 생각해냈는데, 간단하게 설명하자면 스무고개 같은 게임이다. 한 명이 문제를 출제하면 다른 사람이 추리하면서 어떠한 사건인지를 밝혀내는 추리형 게임인데 생각해보니 이 게임의 유명도에 비해 앱이나 웹이 존재하지 않는 것을 깨달았다.

 

그렇게 두 번째 모험이 시작되었다. 정식 출시나 공식적인 앱은 아니지만 추후에 어떠한 팀을 꾸려 제대로 된 개발을 거쳐 시장에 출시한다면 분명 수요가 있을 거라 생각하면서 혼자서 맛보기 스푼용 바다거북수프 앱을 만들어 보도록 하자!

 


클래스와 레이아웃의 구성

 

이 앱은 간단하게 로그인과 회원가입을 처리할 수 있는 기능3개의 모드를 통해서 게임을 즐길 수 있도록 목표를 잡고 개발을 하기 시작했었다. 첫 번째로는 앱의 흐름도를 간단하게 구성을 하기 시작하였고 작성을 완료한 흐름도를 토대로 각각의 액티비티의 기능과 디자인 등을 스케치를 통해 잡아주었다. 뜬금없긴 하지만 필자의 경우 맥북과 갤럭시탭을 같이 사용하고 있는데 둘의 케미도 나쁘지 않은 것 같다. 마이크로소프트 365를 사용 중이다 보니 oneDrive를 1TB 사용 중인데 덕분에 탭에서 제작한 문서나 스케치 파일을 자연스럽게 맥북과 주고받을 수 있다.

 

데이터베이스의 경우에는 MYSQL 이 아닌 SQLite를 이용하여 DB를 구축하였는데 왜냐고 물어본다면 사실 최근 학습에서 SQLite를 사용하였는데 그러다 보니 연습도 해볼 겸 SQLite를 사용한 것이다. 정말 보잘것없는 이유이다,, ^-^

 

이 앱은 프레그먼트를 이용하지 않고 오로지 액티비티 이동으로만 만든 앱이 되다 보니 많은 클래스와 레이아웃이 존재하게 되었는데 간단하게 살펴보자면 로그인과 회원가입을 할 수 있는 화면, 3가지 모드 중 하나를 고를 수 있는 메인 화면, 그리고 각각의 모드에서 기능들이 담겨있는 화면 등으로 구성되어 있다. 

 

모드는 총 3가지로 플레이 모드, 출제자 모드, 창작 모드가 되겠다. 플레이 모드의 경우 실시간으로 게임 방을 생성 후에 실시간으로 채팅할 수 있는 기능을 넣어보고 싶었지만 역시 이 경우는 실제 서버 및 채팅 프로그램을 구현해야 하기 때문에 나로서는 무리라고 판단하여 음성인식 통해 텍스트를 입력받고 출력해주는 기능을 넣어보았다. 채팅과 음성인식이 무슨 상관이냐고? 아무 상관없다. 그냥 음성인식 기능을 앱에 한번 넣어보고 싶어서 넣어봤다. 크크

 

 

출제자 모드의 경우 앱 내에서 사람들에게 출제하고 싶은 문제의 장르를 선택하고 선택한 장르에서 앱 내에 들어있는 문제들을 가져와 화면에 출력해주고 정답까지 확인할 수 있는 기능을 구현했다. 앱 내의 문제들은 DB를 사용하지 않고 TextView 안에 직접 문제들을 써넣어주었다. 왜 DB를 사용하지 않았냐고 하면 만들다 보니 그렇게 할 생각을 못했다. 그냥 바보다.

 

창작 모드는 앱을 사용하는 유저들이 직접 문제를 작성하여 저장할 수 있는 기능을 넣어주었다. 이는 SQLite를 이용하여 각각의 문제들을 저장해 주었다. 간단한 수정 기능 또한 같이 구현해주었다.

 

로그인과 회원가입의 클래스의 경우 어느 프로그램이 그렇듯 이미 만들어진 테이블 및 SQL문을 통하여 작성되기에 비슷한 형식이라 생각되기에 여기서는 또 다루지는 않겠다. 안드로이드이다 보니 if 문을 통해 로그인 성공 및 실패 등과 같은 경우로 인텐트 전송에만 다른 부분이 있지 거의 비슷비슷한 것 같다.

 


MainActivity.java

package com.example.ltpproject;

import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

public class MainActivity extends AppCompatActivity {

    Button playButton;
    Button startButton;
    Button createButton;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        playButton = findViewById(R.id.playModeBtn);
        startButton = findViewById(R.id.startModeBtn);
        createButton = findViewById(R.id.createModeBtn);

        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        String id = bundle.getString("id");

        // Toolbar
        Toolbar mtoolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mtoolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setTitle("안녕하세요, " + id +" 님"); // 인텐트로 아이디(닉네임) 가져와서 툴바의 타이틀 설정

        playButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getApplicationContext(),PlayModeActivity.class);
                startActivity(intent);
            }
        });

        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getApplicationContext(),startModeMain.class);
                startActivity(intent);
            }
        });

        createButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getApplicationContext(), createModeMain.class);
                startActivity(intent);
            }
        });
    } // onCreate

    // ToolBar 뒤로가기
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case android.R.id.home:{ //toolbar의 back키 눌렀을 때 동작
                finish();
                return true;
            }
        }
        return super.onOptionsItemSelected(item);
    }
}

 

로그인 성공 후 나오는 메인화면의 클래스를 살펴보면 매우 간단하게 구성되어있다. 모든 클래스에는 툴바를 생성 및 뒤로 가기를 구현한 코드가 있으며 각각의 버튼을 생성 후 클릭했을 시 인텐트를 생성 후 어느 액티비티로 이동할지 잡아주기만 하면 되었다. 메인 액티비티에서 귀여운 기능 중에 하나라면 로그인한 id 값을 인텐트로 전송받아서 setTitle 해주었다.

 

여기서 playButton을 클릭하였을 경우 이동한 PlayModeActivity의 레이아웃 구성 및 주요 코드를 살펴보자!

 

 

플레이 모드의 화면

 

간단하게 화면 구성을 보면 툴바를 통해 어느 모드인지 확인 할 수 있고 뒤로가기 버튼이 있으며 메인에는 클릭시 음성인식을 하는 이미지와 글을 전부 지울수 있는 지우기 버튼으로 이루어져있다. 그리고 중앙에 빈 공간을 통해 음성인식을 통해 입력받은 값을 비어있는 텍스트 필드안에 설정해준다.

 

 

PlayModeActivity.java

    if(ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) !=
            PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.INTERNET) !=
            PackageManager.PERMISSION_GRANTED ) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.INTERNET, Manifest.permission.RECORD_AUDIO}, 100);
        return;
    }
    prepareSttNTts();
}

 

음성인식 기능을 사용하기 위해선 AndroidManifest에 uses-permission 및 queries를 선언해주어야 정상적인 사용이 가능하다. 그 이후 클래스 내부에서 음성인식을 위한 동의를 받았는지, 받지 않았다면 어떻게 하는지를 처리해주는 코드이다. 당연히 동의를 받았을 때에만 음성인식이 가능하게끔 구현해놓았다.

 

speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
intentB = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intentB.putExtra(RecognizerIntent.EXTRA_LANGUAGE,"ko-KR");
intentB.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,getPackageName());
intentB.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, false);
intentB.putExtra("android.speech.extra.DICTATION_MODE",true);

 

그 후 이미 선언해둔 speechRcognizer를 가져와 생성해주고 intentB에 putExtra를 통해 한국어를 인식하게끔 설정해 주었다.

 

speechRecognizer.setRecognitionListener(new RecognitionListener() {

 

speechRecoginzer를 오버라이드 한 후 여러 가지 설정을 해줄 수 있으며 필자의 경우 음성인식 중일 때에 "음성인식 중입니다!"라는 문구가 뜨며, 여러 가지 오류 중 음성인식 권한에 대한 오류, 네트워크에 대한 오류만 따로 텍스트가 뜨게끔 설정해두었고 나머지 오류는 오류 번호가 뜨게끔 설정해주었다.

 

@Override
public void onResults(Bundle bundle) {
    ArrayList<String> sttResults = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
    if(sttResults != null && sttResults.size() > 0){
        textStt.append(sttResults.get(0)+"\n"); // 음성인식 후 텍스트 설정
        textStatus.setText("");
    }
}

 

마지막으로 오버라이드 한 onResults 메소드를 통해 완료된 음성을 ArrayList를 통해 설정해주는데 textStt라는 텍스트필드에 한 줄씩 설정하기 위해 개행을 넣어주고 음성인식 상태를 나타내 주던 textStatus를 초기화해 아무 글자도 보이지 않게끔 설정해주었다.

 

그리고 아래 더보기를 눌러 다음 코드를 확인해보자면

 

더보기

작성하다 보니 글이 너무 길어지는 것 같아 여러 편으로 나누는 게 좋을 것 같다. 급하게 제목에 -1-을 붙이고 일단 이번 편은 도망가도록 하겠다.

일단 튀어~!~!~!