난독화를 하는 데 사용되었던 코드를 기록.     (자바)   (SVN, 난독화 관련)

 

 

요약

1. SVN 로그인, 해당 파일 다운로드 JAVA 코드

2. window 혹은 linux에 CLI 명령어 입력하는 JAVA코드

3. 파일에 text입력하는 JAVA 코드

4. 원하는 파일만 문자열로 받아 나머지는 삭제하는 JAVA코드 

5. 빈폴더 삭제하는 JAVA코드 

6. 압축하여 다운로드 받는 JAVA코드

 

 

 


 

            DAVRepositoryFactory.setup();
            String url = ---------url--------------;
            String name =---------id------------;
            String password = -----------pw----------;
            SVNRepository repository = null;
            try {
                repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(url));
                ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(name, password);
                repository.setAuthenticationManager(authManager);
                repository.testConnection();
            } catch (SVNAuthenticationException e) {
                return -1;
            } catch (SVNCancelException e) {
                return -2;
            }

자바 구현체에 url, id, pw를 받아서 SVN에 로그인이 가능한지 확인하는 코드.

(아이디가 없으면 -1을 return한다.)

 

 

 


 

        SVNURL url = -----------------url----------------------;
        SVNClientManager clientManager = SVNClientManager.newInstance(SVNWCUtil.createDefaultOptions(true), id, pw);
        File localRepositoryDir = new File(-----------directory---------------------);
        try {
            long checkoutRevision = clientManager.getUpdateClient().doCheckout(url, localRepositoryDir, SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.INFINITY, true);
        } catch (SVNAuthenticationException e) {
            result = -1;
        } catch (SVNCancelException e) {
            result = -2;
        }

지정된 SVN url에 권한이 있는지 확인 후 해당 url의 파일들을 directory경로에 다운로드 받아줌.

(아이디 혹은 권한이 없으면 -1 반환)

 

 

 


 

    public static void execute(String cd, String path, String command, String source, String output, String savePath, String domain) {
        Process process = null;
        Runtime runtime = Runtime.getRuntime();
        StringBuffer successOutput = new StringBuffer(); // 성공 스트링 버퍼
        StringBuffer errorOutput = new StringBuffer(); // 오류 스트링 버퍼
        BufferedReader successBufferReader = null; // 성공 버퍼
        BufferedReader errorBufferReader = null; // 오류 버퍼
        String msg = null; // 메시지

        List<String> cmdList = new ArrayList<String>();

        // 운영체제 구분 (window, window 가 아니면 무조건 linux 로 판단)
        if (System.getProperty("os.name").indexOf("Windows") > -1) {
            cmdList.add("cmd");
            cmdList.add("/c");
        } else {
            cmdList.add("/bin/sh");
            cmdList.add("-c");
        }

        cmdList.add(cd + " " + path + " " + "&&" + " " + command + " " + source + " " + output + " " + savePath + " " + " " + domain);

        String[] array = cmdList.toArray(new String[cmdList.size()]);

        try {

            // 명령어 실행
            process = runtime.exec(array);

            // shell 실행이 정상 동작했을 경우
            successBufferReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "EUC-KR"));

            while ((msg = successBufferReader.readLine()) != null) {
                successOutput.append(msg + System.getProperty("line.separator"));
            }

            // shell 실행시 에러가 발생했을 경우
            errorBufferReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "EUC-KR"));
            while ((msg = errorBufferReader.readLine()) != null) {
                errorOutput.append(msg + System.getProperty("line.separator"));
            }

            // 프로세스의 수행이 끝날때까지 대기
            process.waitFor();

            // shell 실행이 정상 종료되었을 경우
            if (process.exitValue() == 0) {
                System.out.println("성공");
                System.out.println(successOutput.toString());
            } else {
                //shell 실행이 비정상 종료되었을 경우
                System.out.println("비정상 종료");
                System.out.println(successOutput.toString());
            }
            // shell 실행시 에러가 발생
            if (errorOutput.length() > 0) {
                //shell 실행이 비정상 종료되었을 경우
                System.out.println(errorOutput.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                process.destroy();
                if (successBufferReader != null)
                    successBufferReader.close();
                if (errorBufferReader != null)
                    errorBufferReader.close();

            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

window 혹은 linux에 CLI 명령어를 입력하는 코드.

 

 


 

 

        String obFile = ----------필요한 파일들 파일명---------------;

        File f = new File(--------svn에서 받아온 파일들 경로----------------);
        ArrayList<String> subFiles = new ArrayList<String>();

        if (!f.exists()) {
            return -1;
        }
        //하위 디렉토리 검색
        findSubFiles(f, subFiles);

        File file = null;

        //파일 화이트리스트에 없으면 삭제
        for (String filePath : subFiles) {
            file = new File(filePath);
            if (file.exists() && file.isFile()) {
                try {
                    if (obFile.contains(file.getName())) {

                        //JS에 주석달기 start----------------------------------------------
                        String dummy = ""; // 새로 써지는 텍스트 저장 변수

                        SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");
                        Date time = new Date();
                        String dt = format1.format(time);

                        String c_dt = "/*20210314*/";
                        String c_obIp = "/*100.000.000*/";
                        String c_obProjectNm = "/*jodonggu*/";
                        String c_userId = "/*jodongddng*/";

                        try {
                            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));

                            String line;

                            for (int i = 0; i < 50000; i++) { // 5만줄까지 제한
                                line = br.readLine(); // 읽으며 이동

                                if (null == line) { // 읽을 것이 없으면 break
                                    break;
                                }

                                String temp_text = "";

                                if (i == 0) { // 첫번째 줄이라면
                                    temp_text = line;
                                    line = c_userId + "\r\n" + c_obProjectNm + "\r\n" + c_obIp + "\r\n" + c_dt + "\r\n" + temp_text; // 새 문장을 넣고 기존문장은 아랫줄에
                                }
                                dummy += (line + "\r\n"); // dummy에 저장
                            }

                            FileWriter fw = new FileWriter(file.getCanonicalPath());
                            fw.write(dummy);

                            fw.close();

                            br.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                      //JS에 주석달기 end----------------------------------------------
                    } else {
                        //삭제
                        File deleteFile = new File(file.getCanonicalPath());
                        deleteFile.delete();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

 

폴더 구조에서 필요한 js파일은 맨 윗줄에 주석을 달아 남기고 

필요 없는 파일은 삭제를 하는 재귀 코드

 

 

 

 

 

 

        //디렉토리에 파일 없으면 삭제
        for (String filePath : subFiles) {
            file = new File(filePath);
            if (file.exists() && file.isDirectory()) {
                try {
                    File fileList = new File(file.getCanonicalPath());
                    String[] files = fileList.list();

                    if (files.length > 0) {
                    } else {
                        fileList.delete();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

그 후 디렉토리에 파일이 없다면 디렉토리를 삭제하여 빈 폴더를 정리 

 

 

 

 

        // 압축
        String zipPath = --------------압축할 zip파일 경로---------------;

        ZipUtil.pack(new File(zipPath), new File(zipPath + ".zip"));

그 후 정리된 폴더를 압축.

압축은 ZipUtil이라는 라이브러리를 다운받아 사용

 

 

 

 

    public void dwFile(Map<String, Object> params) throws Exception {

        OutputStream outStream = null;
        FileInputStream fis = null;
        StringBuffer sb = new StringBuffer();
        sb.append(--------------zip경로 -----------------------);
        sb.append(File.separator);
        String target = ---------zip 파일 이름 -------------------------;
        try {
            outStream = response.getOutputStream();
            File file = new File(sb.toString() + target + ".zip");
            fis = new FileInputStream(file);
            response.setContentType("application/zip");
            DownloadUtils.setDisposition(target + ".zip", request, response);
            //response.setHeader("Content-Transfer-Encoding", "binary");
            String downFileNm = new String(target.getBytes("UTF-8"), "ISO-8859-1");
            response.setHeader("Content-Disposition", "attachment; filename=" + downFileNm + ".zip;");
            FileCopyUtils.copy(fis, outStream);
            outStream.flush();
            response.setHeader("Set-Cookie", "fileDownload=true; path=/");

        } catch (Exception e) {
            LOGGER.error("파일 다운로드 오류");
            throw new BusinessException("파일 다운로드에 실패하였습니다.");

        } finally {
            if (fis != null) {
                fis.close();
            }
            if (outStream != null) {
                outStream.close();
            }
        }
    }

마지막으로 zip 파일 다운로드 받기 

'Project > obfuscator' 카테고리의 다른 글

javascript-obfuscator 난독화 1  (0) 2021.05.16

코드를 난독화 하는 프로젝트를 하게 되었다.

 

그 일련의 과정들 

 

(CLI로 하였을 때)


1. 난독화를 하기 위해 난독화 오픈소스 사용

https://github.com/javascript-obfuscator/javascript-obfuscator

 

javascript-obfuscator/javascript-obfuscator

A powerful obfuscator for JavaScript and Node.js. Contribute to javascript-obfuscator/javascript-obfuscator development by creating an account on GitHub.

github.com


2.  오픈소스를 위해 Ubuntu에 npm 설치

https://jjeongil.tistory.com/1275
https://d2fault.github.io/2018/04/30/20180430-install-and-upgrade-nodejs-or-npm/


3. 파일을 난독화 후 압축하여 다운로드를 하기 위해 ZIP 설치

https://araikuma.tistory.com/120


4. 설치를 다 하였지만 명령어가 실행되지 않음

https://stove99.github.io/nodejs/2019/09/30/yarn-global-command-not-found/ 

yarn 을 사용해 global 로 패키지 설치 후 설치한 패키지를 커맨드창에서 실행했을때 명령어를 찾을 수 없다면서 command not found 에러가 나는 경우가 있다.

npm i -g 와는 다르게 yarn 을 추가적으로 설정이 살짝 필요하다.

.bashrc 수정

.bashrc 파일 맨 끝에 PATH 추가

vi ~/.bashrc

# 맨 끝에 PATH 추가

export PATH="$PATH:`yarn global bin`"

# 저장후 나오기

# 변경내용 적용

source ~/.bashrc

 

이건 yarn을 사용할 때 하는 방법 


5. 그러나 여전히 안됨 (다시 npm으로 시도)

증상 : javascript-obfuscator: command not found


/root/node_modules/javascript-obfuscator/bin 위치에  javascript-obfuscator 파일이 있는 것을 발견

 

vi ~/.bashrc
export PATH="/root/node_modules/javascript-obfuscator/bin:$PATH"

source ~/.bashrc


https://asung123456.tistory.com/30

 

-명령어 등록 성공 -

 


6. 한글이 깨져서 다운로드 경로를 못찾음 

 

https://huskdoll.tistory.com/74
String downFileNm = new String(target.getBytes("UTF-8"), "ISO-8859-1");

 

자바단에서 Encoding 해줌 

 


7. SVN에 로그인을 해서 파일을 받아오는데 연결이 안됨

 

https://m.blog.naver.com/PostView.nhn?blogId=amabile29&logNo=221522544699&proxyReferer=https:%2F%2Fwww.google.com%2F

 

telnet명령어를 써서 연결이 안되는 것을 확인하고 방화벽이 막혀있는 것을 알게됨

내부 아이피를 알아내서 내부 아이피로 연결함 

 

 


8. 난독화를 할 때 사용자에게 이메일을 보내야 하는데 로컬에서 잘 되던게 was서버에서는 안됨

error :

이메일 정보 확인이 필요합니다.

Mail server connection failed; nested exception is javax.mail.MessagingException: Could not convert socket to TLS;
  nested exception is:
javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate). Failed messages: javax.mail.MessagingException: Could not convert socket to TLS;
  nested exception is:
javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

 

 

해결

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> 
<property name="host" value="smtp.gmail.com" /> 
<property name="port" value="465" /> <!-- 587 -->
<property name="username" value="${mailSender.gmailId}" /> 
<property name="password" value="${mailSender.gmailPw}" /> 
<property name="javaMailProperties"> 
<props> 
                <prop key="mail.smtp.auth">true</prop> 
                <prop key="mail.smtp.starttls.enable">true</prop>
                <prop key="mail.smtp.ssl.enable">true</prop>
                <prop key="mail.smtp.ssl.protocols">TLSv1.2</prop>
</props> 
</property> 
</bean>

 

port를 465로 prop key를 

                true</prop key="mail.smtp.ssl.enable">
                TLSv1.2</prop key="mail.smtp.ssl.protocols">

두 줄 추가 


 

themewagon.com/themes/free-bootstrap-4-html5-ecommerce-website-template-freshshop/

 

FreshShop - Free Bootstrap 4 HTML5 Ecommerce Website Template

FreshShop is a free Bootstrap 4 HTML5 ecommerce website template. It comes with a lot of powerful and trendy features like hero header, slider, drop-down menu, call to action button, on hover effect, tabbed content, sticky navigation, to name a few.

themewagon.com

 

 

ThemeWagon은 개발자가 필요로 할 수있는 모든 종류의 템플릿 또는 테마를 포괄하는 다양한 카테고리 중심의 무료 및 프리미엄 반응 형 부트 스트랩 HTML 템플릿과 WordPress 테마를 제공한다.

'Project > 일인 식탁 2020.2~.03' 카테고리의 다른 글

일인식탁 프로젝트  (0) 2020.08.13

 

아이디에 영어와 숫자만 가능하며 10글자 이내로만 아이디를 받기로 하였다.

 

edit_id.filters = arrayOf(InputFilter { source, _, _, _, _, _ ->
            val ps: Pattern =
                Pattern.compile("^[a-zA-Z0-9\\u318D\\u119E\\u11A2\\u2022\\u2025a\\u00B7\\uFE55]+$")
            if (source == "" || ps.matcher(source).matches()) {
                return@InputFilter source
            }
            Toast.makeText( this, "영문, 숫자만 입력 가능합니다.", Toast.LENGTH_SHORT).show()
            ""
        }, InputFilter.LengthFilter(10))

 

A-Za-z0-9는 영어와 숫자만 입력이 가능하게 하는 정규식 특수문자이고 조건에 맞지 않으면 메시지를 띄운다.

 

\\u318D\\u119E\\u11A2\\u2022\\u2025a\\u00B7\\uFE55 이것은 천지인 키보드에 맞게 정의한 입력방식이다. 

 

LengthFilter로 10글자까지만 입력 가능하도록 하였다.

logout 버튼을 클릭 시 다이얼로그를 띄워 재확인한다.

 

로그아웃 버튼을 클릭하였을 때 다이얼로그를 띄운다.

No를 클릭시 null값으로 다이얼로그가 내려가며

Yes클릭 시 로그아웃이 되었다는 메시지와 간단하게 login_activity로 이동하여 메인화면인 로그인 창으로 넘어가도록 하였다.

button_logout.setOnClickListener { view ->
            var dialog = AlertDialog.Builder(this)
            dialog.setTitle("로그아웃을 하시겠습니까?")
            dialog.setMessage("저희 SAFE FARM을 이용해주셔서 감사합니다.")
            dialog.setIcon(R.drawable.icon5)

            fun toast_p() {
                Toast.makeText(this, "로그아웃 되었습니다.", Toast.LENGTH_SHORT).show()
                    val intent = Intent(this,login_activity::class.java)
                    startActivity(intent)
            }
            var dialog_listener = object: DialogInterface.OnClickListener{
                override fun onClick(dialog: DialogInterface?, which: Int) {
                    when(which){
                        DialogInterface.BUTTON_POSITIVE ->
                            toast_p()
                    }
                }
            }
            dialog.setPositiveButton("YES",dialog_listener)
            dialog.setNegativeButton("NO",null)
            dialog.show()
        }

 

 

1.5초 내로 두 번 뒤로 가기를 눌렀을 경우 토스트 메세지를 띄우고 앱을 종료시키는 코드이다.

필요한 각 activity에 코드를 넣었다.

var lastTimeBackPressed : Long = 0
    override fun onBackPressed() {
        if(System.currentTimeMillis() - lastTimeBackPressed >= 1500){
            lastTimeBackPressed = System.currentTimeMillis()
            Toast.makeText(this,"'뒤로' 버튼을 한번 더 누르시면 종료됩니다.",Toast.LENGTH_LONG).show() }
        else {
            ActivityCompat.finishAffinity(this)
            System.runFinalization()
            System.exit(0)
        }
    }

 

이렇게 카드뷰를 만들었다.

 

 

 

 

build.gradle(Module: app)

implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'

2번째의 implementation의 경우 버전이 안 맞아서 레이아웃에 에러가 나더라 

1번째의 implementation으로 겨우 성공했다. 

 

build.gradled에서 Sync Now를 하고 implementation에 빨간줄이 뜬다고 무서워하지 마라 

Build의 SUCCESSFUL이 뜨면 성공이다. (나는 오류인줄알고 무서워서 이리저리 만짐...)

 

 

 

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@drawable/wave"
    android:orientation="vertical"
    app:cardUseCompatPadding="true"
    >


    <TextView
        android:id="@+id/title_catagory"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="50dp"
        android:layout_marginTop="30dp"
        android:text="Farm"
        android:textColor="#fff"
        android:textSize="30dp"
        android:textStyle="bold" />

    <Button
        android:id="@+id/button_logout"
        android:layout_width="100dp"
        android:layout_height="40dp"
        android:layout_marginLeft="280dp"
        android:background="@drawable/round_bt"
        android:text="Logout"
        android:textColor="#FFFFFF"
        android:textSize="12sp"
        android:textStyle="bold"
        />

    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="20dp"
        android:columnCount="2"
        android:rowCount="3">

        <androidx.cardview.widget.CardView
            android:id="@+id/cardview1"

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_row="0"
            android:layout_rowWeight="1"
            android:layout_column="0"
            android:layout_columnWeight="1"
            android:layout_gravity="fill"
            android:layout_margin="8dp"
            android:background="#FFFFFF"
            android:elevation="40dp"
            app:cardCornerRadius="15dp">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical|center_horizontal"
                android:gravity="center"
                android:orientation="vertical">

                <ImageView
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:src="@drawable/icon1" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="FARM1"
                    android:textAlignment="center"
                    android:textStyle="bold" />


            </LinearLayout>
        </androidx.cardview.widget.CardView>

        <androidx.cardview.widget.CardView
            android:id="@+id/cardview2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_row="0"
            android:layout_rowWeight="1"
            android:layout_column="1"
            android:layout_columnWeight="1"
            android:layout_gravity="fill"
            android:layout_margin="8dp"
            android:background="#FFFFFF"
            app:cardCornerRadius="15dp"
            app:cardElevation="8dp">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical|center_horizontal"
                android:gravity="center"
                android:orientation="vertical">

                <ImageView
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:src="@drawable/icon" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="FARM2"
                    android:textAlignment="center"
                    android:textStyle="bold" />


            </LinearLayout>
        </androidx.cardview.widget.CardView>

        <androidx.cardview.widget.CardView
            android:id="@+id/cardview3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_row="1"
            android:layout_rowWeight="1"
            android:layout_column="0"
            android:layout_columnWeight="1"
            android:layout_gravity="fill"
            android:layout_margin="8dp"
            android:background="#FFFFFF"
            app:cardCornerRadius="15dp"
            app:cardElevation="8dp">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical|center_horizontal"
                android:gravity="center"
                android:orientation="vertical">

                <ImageView
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:src="@drawable/icon2" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="FARM3"
                    android:textAlignment="center"
                    android:textStyle="bold" />


            </LinearLayout>
        </androidx.cardview.widget.CardView>

        <androidx.cardview.widget.CardView
            android:id="@+id/cardview4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_row="1"
            android:layout_rowWeight="1"
            android:layout_column="1"
            android:layout_columnWeight="1"
            android:layout_gravity="fill"
            android:layout_margin="8dp"
            android:background="#FFFFFF"
            app:cardCornerRadius="15dp"
            app:cardElevation="8dp">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical|center_horizontal"
                android:gravity="center"
                android:orientation="vertical">

                <ImageView
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:src="@drawable/icon3" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="FARM4"
                    android:textAlignment="center"
                    android:textStyle="bold" />


            </LinearLayout>
        </androidx.cardview.widget.CardView>

        <androidx.cardview.widget.CardView
            android:id="@+id/cardview5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_row="2"
            android:layout_rowWeight="1"
            android:layout_column="0"
            android:layout_columnWeight="1"
            android:layout_gravity="fill"
            android:layout_margin="8dp"
            android:background="#FFFFFF"
            app:cardCornerRadius="15dp"
            app:cardElevation="8dp">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical|center_horizontal"
                android:gravity="center"
                android:orientation="vertical">

                <ImageView
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:src="@drawable/icon4" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="FARM5"
                    android:textAlignment="center"
                    android:textStyle="bold" />


            </LinearLayout>
        </androidx.cardview.widget.CardView>

        <androidx.cardview.widget.CardView
            android:id="@+id/cardview6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_row="2"
            android:layout_rowWeight="1"
            android:layout_column="1"
            android:layout_columnWeight="1"
            android:layout_gravity="fill"
            android:layout_margin="8dp"
            android:background="#FFFFFF"
            app:cardCornerRadius="15dp"
            app:cardElevation="8dp">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical|center_horizontal"
                android:gravity="center"
                android:orientation="vertical">

                <ImageView
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:src="@drawable/icon5" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="FARM6"
                    android:textAlignment="center"
                    android:textStyle="bold" />


            </LinearLayout>
        </androidx.cardview.widget.CardView>
    </GridLayout>
</LinearLayout>

 

그 뒤에 androidx.cardview.widget.CardView 를 사용하여 그 안에 카드뷰를 꾸미면 된다.

 

 

app:cardCornerRadius="15dp"
app:cardElevation="8dp"

참고로 CornerRadius는 카드의 모서리를 둥글게 하는것

Elevation 카드의 그림자이다.

(나는 코드를 적었는데도 화면상의 변화가 없길래 당황했는데 알고보니 Run 'app' 을 실행시키니 변화가 생겼다.

가상머신이나 폰으로 실행시켜보자.)

 

github.com/parksung-woo/4JO.git

 

parksung-woo/4JO

4JO test. Contribute to parksung-woo/4JO development by creating an account on GitHub.

github.com

 

 

 

 

README.md

 

 

프로젝트 개요 : 농장의 일은 온도와 습도 같은 환경에 예민하게 반응한다.
               개발한 프로젝트의 어플리케이션으로 온도와 습도를 관련하여 농장을 편리하게 관리할 수 있도록 하기 위해 제작을 결심했다.
               
프로젝트 소개: 안드로이드 App에 접속하여 회원가입을 진행 후 농장을 만들어 해당 농장의 온도,습도정보를 확인할 수 있다.
              온도의 따라 라즈베리파이에 연결된 Fan을 원격으로 동작시켜 온도를 낮출 수 있고, 
              그래프와 카메라를 이용하여 농장의 온도변화와 실시간 화면을 확인할 수 있다.
              웹의 관리자 화면에서는 관리자 비밀번호를 입력 후 회원가입한 회원정보 리스트를 확인할 수 있다.
              회원정보 리스트에서 회원정보를 수정 및 삭제 가능

      App기능  
                  1. 회원가입
                   - ID, password, password_confirm, nickname입력 후 회원가입
                     (primary key:ID, password = password_confirm, 모든 빈칸을 채워야 회원가입 가능)
                     (ID:최대 10글자 가능. password:최대 12글자 가능, nickname: 최대 3글자 가능)
                  2. DB에 실시간으로 저장된 온도, 습도 정보 확인
                     (이상 수치 발생 시 log저장 후 푸시 알림 및 Fan 자동 작동)
                  3. 스위치 버튼 클릭 시 라즈베리파이에 연결된 DC 모터를 이용하여 원격 Fan 수동 작동
                  4. 그래프를 통한 온도 확인
                  5. 라즈베리파이에 연결된 카메라를 통해서 실시간 농장 확인
      
      웹 기능(관리자 화면)
                  1. 관리자 비밀번호 입력 후 Member list 출력 화면
                  2. Member list에서 회원정보 수정 및 삭제 가능
               
      Local에서 돌리기 위한 매뉴얼
                  1. 안드로이드 App : android client 폴더 - team4Android 실행 (Size=Picel 3XL, Minimum SDK=API 22:Android 5.1(Lollipop)) 
                  2. Spring boot: backend 폴더 - service 실행(Intellij(Ultimate Version))
                    (본인의 AWS DB 주소 및 local ip주소 변경 후 RUN)
                  
                  
    #개발환경
        -Web
        * SERVER : TOMCAT 11.0
        * DATABASE : MariaDB 10.3.20(AWS EC2 RDS생성)
        * IDE : Intellij(Ultimate Version)
        * FrontEnd : Bootstrap, Javascript, HTML5, CSS3
        * Language & Framework : Java 8, Spring boot 2.3.1

        -App
        * Language & Framework : Kotlin , Anroid Studio(compileSdkVersion 30, minSdkVersion 22)
        * DATABASE : MariaDB 10.3.20             

        * Git(2.27.0) & Sourcetree, Slack 메신저, oven 스토리 보드 활용

        * 라즈베리파이 Model 3 B

 

 

 

 

 

 

+ Recent posts