">

CLOSE_WAIT



  그림에서 Client가 연결을 종료하기 위해 FIN을 Server로 보낸다. 그리고 FIN에 대한 ACK가 있기 전까지 Server은 CLOSE_WAIT 상태가 된다. 

CLOSE_WAIT 상태가 사라지지 않고 계속 쌓이게 되면 서버에서는 더 이상 서비스 처리를 못하는 상태가 된다. 

CLOSE_WAIT 상태를 해결하는 방법을 시스템의 설정을 바꾸는 방법에 대한 설명이 많았는데 이렇게는 근본적인 원인을 해결할 수 없다. 


  여러 방법들이 있겠지만, 개인적으로 겪은 부분만 정리해 본다. 문제의 원인은 'DefaultHttpClient' 때문이었다. 

사용하는 부분에서 잘못된 것은 없었지만 CLOSE_WAIT 현상이 발생했다. 

그리고 같은 소스로 여러 서버에서 테스트를 진행했을 때 이 현상이 발생하는 곳도 있고, 

어느정도 CLOSE_WAIT가 쌓이다가 없어지는 서버도 있었다. 그래서 변경한 부분은 'DefalutHttpClient'를 '

CloseableHttpClient'로 변경했다. 거짓말처럼 말끔하게 CLOSE_WAIT가 사라졌다. 

DefaultHttpClient를 꼭 사용해야 한다면 아래 참고 자료 중 2번에 해당하는 링크를 참조하면 된다. 

그리고 읽어보면 좋을 것 같아서 추가한 것도 있다. 아래 참고 자료 3번에 해당하는 링크를 참조하면 된다. 

  


문제: CLOSE_WAIT가 쌓이는 현상 발생


해결: DefaultHttpClient -> CloseableHttpClient



참고 자료

[1] http://docs.likejazz.com/close-wait/

[2] http://pchun.tistory.com/60

[3] http://d2.naver.com/helloworld/377316

'프로그래밍 > JAVA' 카테고리의 다른 글

RSA 암호화 및 복호화  (0) 2016.12.26
자바로 크롤링 구현  (0) 2016.09.13
문자열을 나누거나 합치거나  (0) 2016.09.13

네트워크 상태를 확인하고, GPS의 상태를 확인하는 코드


우선 네트워크 상태를 확인하는 코드

boolean isOnline = false;

try {
		ConnectivityManager conMan = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
			
		State wifi = conMan.getNetworkInfo(1).getState();
		if(wifi == NetworkInfo.State.CONNECTED) {
			isOnline = true;
		}
			
		State mobile = conMan.getNetworkInfo(0).getState();
		if(mobile == NetworkInfo.State.CONNECTED) {
			isOnline = true;
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}


GPS 상태를 확인하는 코드

boolean gpsEnable = false;
LocationManager manager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
if(manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
	gpsEnable = true;
}


Spring에서 RSA 암호화를 사용하려는 도중 에러가 발생 했다. 


'nested exception is java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider'


필요한 jar도 추가해주고 build path도 잡아줬는데도 왜!!!?


검색해 보니 Java 폴더를 찾아서 jar 파일을 복사하면 된다고 한다. 


파일 경로는 jdk 폴더 안에 /Contents/Home/jre/ext 이다. (Mac 기준)


개발을 하면서 암호화에 대해 깊이 있게 생각하지 않고 필요한 스펙 대로만 구현 했었는데, 이번에 시간이 좀 있어 블로그에 정리를 해봅니다. 

이 글은 Java 기준으로 작성하였습니다. 


여러 글을 보면서 검색을 하다 보니 타 업체와 협의 할 때 적어도 아래와 같은 스펙을 서로 교환하는게 좋다고 합니다. (http://redutan.github.io/2015/11/20/about-crypto)



암호화 스펙

    • 알고리즘: RSA-256
    • 암호화 키: 2017201820192020wow2021202220232024202520262027202820292030
    • 인코딩: UTF-8


public class RSAEncrypto {
	private static final String algorithm = "RSA/None/NoPadding";
	private static final String provider = "BC";

	public static void main(String[] args) {
		
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

		String plainText = "2017201820192020wow2021202220232024202520262027202820292030";

		try {
			Cipher serverCipher = Cipher.getInstance(algorithm, provider);
			SecureRandom random = new SecureRandom();
			KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
			generator.initialize(512, random);
			
			KeyPair pair = generator.generateKeyPair();
			Key publicKey = pair.getPublic();  
			Key privateKey = pair.getPrivate();

			// 개인키로 암호화
			serverCipher.init(Cipher.ENCRYPT_MODE, privateKey);
			byte[] cipherText = serverCipher.doFinal(plainText.getBytes());
			System.out.println("cipher: ("+ cipherText.length +")"+ new String(cipherText));

			// 공개키로 복호화
			serverCipher.init(Cipher.DECRYPT_MODE, publicKey);
			byte[] plainTextB = serverCipher.doFinal(cipherText);

			System.out.println("plain: " + new String(plainTextB));
			
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		}
	}
}


이렇게 하면 암호화 하고, 복호화 되는 것을 확인할 수 있습니다. 하지만 실무에서는 이렇게 사용하는 경우는 거의 없습니다. 그래서 추가로 필요한 부분이 있습니다. 바로 Base64로 인코딩을 해야 되는 것입니다. 이상한 문자로 나온 암호문을 통신 할 때나, DB에 저장해서 사용할 수 없기에 Base64로 인코딩 합니다. 그리고 복호화 할 때는 다시 디코딩을 해서 복호화를 진행합니다. 


참고로 Base64는 8비트 바이너리 데이터를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 스트링으로 바꾸는 인코딩 방식을 말합니다. 아래 그림을 보면 Base64로 인코딩 된 것은 우리가 알아볼 수 있는 문자로 출력 됩니다. 




인코딩을 사용하여 적용한 소스는 다음과 같습니다. 

public class RSAEncrypto {
	private static final String algorithm = "RSA/None/NoPadding";
	private static final String provider = "BC";

	public static void main(String[] args) {
		
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

		String plainText = "2017201820192020wow2021202220232024202520262027202820292030";

		try {
			Cipher serverCipher = Cipher.getInstance(algorithm, provider);
			SecureRandom random = new SecureRandom();
			KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
			generator.initialize(512, random);
			
			KeyPair pair = generator.generateKeyPair();
			Key publicKey = pair.getPublic();  
			Key privateKey = pair.getPrivate();

			// 개인키를 전달하여 암호화
			serverCipher.init(Cipher.ENCRYPT_MODE, privateKey);
			byte[] cipherText = serverCipher.doFinal(plainText.getBytes("UTF-8"));
			System.out.println("cipher: ("+ cipherText.length +")"+ new String(cipherText));
			
			// Base64로 인코딩
			byte[] encodedBase64 = Base64.encode(cipherText);
			System.out.println("Base64Encoded: " + new String(encodedBase64));
			
			// Base64로 디코딩
			byte[] decodedBase64 = Base64.decode(encodedBase64);
			System.out.println("Base64Decoded: " + new String(decodedBase64));
			
			// 공개키를 가지고있는쪽에서 복호화
			serverCipher.init(Cipher.DECRYPT_MODE, publicKey);
			byte[] plainTextB = serverCipher.doFinal(decodedBase64);
			
			System.out.println("plain: " + new String((plainTextB), "UTF-8"));
			
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


패딩을 사용 하는 이유에 대해서는 다음 링크를 참조하시면 됩니다. 
여러 이유에 대한 설명이 있으니 꼭 읽어보세요. 
https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Padding_schemes

마지막으로 RSA 키를 저장하고 복구 하는 것에 대한 설명은 링크로 대체 하겠습니다. 


도움 되셨다면 아래 '공감' 클릭 한번 해주세요~ 감사합니다. 


'프로그래밍 > JAVA' 카테고리의 다른 글

CLOSE_WAIT 해결 방법  (1) 2017.01.13
자바로 크롤링 구현  (0) 2016.09.13
문자열을 나누거나 합치거나  (0) 2016.09.13

  프로젝트를 진행하다보면 복잡하지 않더라도 협업을 해야하는 경우가 발생하죠. 

혹은 추가 개발 또는 유지 보수 업무를 맡게 되면 ERD가 없을 경우 시스템에 대해 설계/분석 하는데 시간이 더 걸리게됩니다. 

Toad for MySQL에서도 리버스 엔지니어링 기능을 가지고 DB의 스키마를 ERD로 표현해주는 기능이 있지만 수정이나 편집이 불가능 해요! 

회사 선배가 알려준 ERWin의 사용법을 이제서야 블로그에 적어봅니다.


  ERWin은 아직 Mac 버전은 없는 것 같아요. 그래서 아래 글은 Windows7(64bit) 기준으로 작성 했어요~


1. PC에 ODBC 설정에서 MySQL용 드라이버 설치 및 연결정보 설정하기!

드라이버 다운로드 Go Go!


2. 다운로드 완료 후에 '시작' 메뉴 옆의 검색 창에 '데이터 원본' 이라고 검색합니다. 


3.  ODBC 데이터 원본 32비트 또는 64비트를 선택 합니다. 그리고 추가 버튼을 클릭해 드라이버를 추가하도록 합니다. 


4. MySQL ODBC 드라이버를 선택하고 마침을 누릅니다. 


5. 항목을 입력 하고 OK 버튼을 누릅니다. 




6. ERWin을 실행 시키고, Reverse Enginnering를 실행합니다. 

설계를 추가해야할지도 모르기에 타입은 논리/물리 모델 두가지를 포함하는게 낫겠죠? 

Target Database를 MySQL로 선택하면 됩니다. 


7. Infer 영역에서 Key와 Relations를 선택하면 테이블 관계 및 키 까지 테이블이 그려질 때 같이 그려져요. 


8. User Name 및 Password를 입력하고 value에서 아까 추가한 ODBC를 선택하고 Connect를 클릭하고 조금 기다리면 테이블이 나옵니닷~


Crawler.zip



  구글 플레이스토어에 있는 앱 이름, 아이콘, 패키지 이름이 필요해서 크롤링해서 사용하려고 만들어 보았습니다. 혹시 필요하신 분은 이거 받으셔서 좀 보강한 뒤에 사용하시면 될 것 같아요. 소스는 압축해서 파일로 올려 놨습니다.  


  우선 URL 클래스와 openStream을 사용해서 지정한 주소의 소스코드를 받아오도록 합니다. 그리고 필요한 부분을 추출하기 위해서 자를 수 있는 부분을 확인 한 다음 문자열을 잘라내면 됩니다. 문자열을 자르고 합치는게 일이지 웹을 긁어오는 건 일이 아니라 생각해요. 문자열을 잘 다루지 못하는 분이면 아래 문자열 자르고 합치는 부분에 대한 설명을 보시면 도움이 될거라 생각합니다.

  

  반복되는 부분이 끝 날때 까지 while이 돌고, while문 안에서 필요한 부분을 추출합니다. 그리고 추출한 다음 다음 문장에서 필요한 것을 추출하기 위해 추출한 부분은 다시 찾지 않도록 문자열의 일부분을 잘라내고 잘라진 문자열에서 패턴으로 필요한 부분을 찾습니다. 


  소스를 돌려보는 편이 빠를거라 생각되요~ 주석도 잘 달아놨으니까 한번 보세요~


2016/09/13 - [프로그래밍/JAVA] - 문자열을 나누거나 합치거나


'프로그래밍 > JAVA' 카테고리의 다른 글

CLOSE_WAIT 해결 방법  (1) 2017.01.13
RSA 암호화 및 복호화  (0) 2016.12.26
문자열을 나누거나 합치거나  (0) 2016.09.13

  문자열로 무엇을 한다고 하면 자르고, 합치고 하는 것이 아닐까요? 웹 크롤링을 하면서 많이 사용되는 부분을 통해 문자열을 자르고 합치는 연습을 해봅시다. 


  우선 문자열을 자르기 위해 어떻게 해야 할까요? 


1. substring(int)


  String str = "good morning"  라는 문자열이 있을 때, substring을 사용하는 것을 보도록 할게요. 

  String sub = str.substring(5) 의 결과는 'moring' 가 됩니다. 




2. substring(int, int)


  동일하게 String str= "good morning"  라는 문자열이 있을 때 어떻게 되는지 보도록 할게요. 

  String sub = str.substring(3, 7) 의 결과는 'd mo' 가 됩니다. 4번째 문자 부터 7번째 문자까지 잘립니다. 



3. indexof 를 사용하여 문자열의 위치 찾기


  동일하게 String str="good morning" 라는 문자열이 있을 때 어떻게 되는지 보도록 할게요. 

  String indexof = str.indexOf("d") 의 결과는 '3'이 나옵니다. 



  substring와 indexOf를 사용하면 크롤링 시 원하는 부분을 잘라 낸 뒤 사용할 수 있습니다. 문자열을 합치는 방법은 간단하게 '+'를 사용하는 분들이 많을거라고 생각해요. 하지만 좋지 않은 방법이라고 말씀드리고 싶습니다. 문자열을 합칠 때는 StringBuilder 나 StringBuffer Class를 사용하여 합치는 것이 좋아요. 


StringBuffer strBuf = StringBuffer();

strBuf.append("I");

strBuf.append(" love");

strBuf.append(" you");


이런 형식으로 사용할 수 있습니다. 



실제 플레이스토어에서 앱 이름, 앱 아이콘, 패키지 이름을 크롤링 하는 부분을 만든게 있는데 확인해보시면 이해하는데 도움이 될것 같네요. 


2016/09/13 - [프로그래밍/JAVA] - 자바로 크롤링 구현


'프로그래밍 > JAVA' 카테고리의 다른 글

CLOSE_WAIT 해결 방법  (1) 2017.01.13
RSA 암호화 및 복호화  (0) 2016.12.26
자바로 크롤링 구현  (0) 2016.09.13

  WWDC 2016 에서 올해 안에 ATS에 대한 발표가 있었다. 앱에서 통신을 할 때 https로 제한한다고 한다. 2016년 말부터 앱스토어에 공개하는 앱은 ATS가 필요하다고 발표 되었다. iOS 9 버전에서는 아래 그림과 같이 설정을 하면, http로 통신이 가능하다.


하지만 iOS 10에서는 NSAllowsArbitrayLoadsinWebContent가 YES인지 체크 하며 WKWebView만 http 통신을 허용하게 된다. 불특정 다수의 서버에 http 통신을 해야 하는 경우 위와 같이 설정하고, 앱 심사 때 이유를 잘 설명해야 한다. 

'프로그래밍 > iOS' 카테고리의 다른 글

둥근버튼 만들기  (0) 2018.04.24
Xcode9 svn 사용하기  (0) 2017.12.13
app icon 사이즈  (0) 2017.11.30
CoreLocation GPS 튜토리얼  (0) 2017.11.10
library not found for -lcrt1.3.1.o 에러 해결 방법  (0) 2017.08.08

AwesomeNavigation.zip


예제는 위에 있는 압축파일 받아서 확인해주세요~


  Navigation Drawer에 대해서 쉽고, 알고 싶은 것을 속 시원히 알려드릴게요. 우선 기본적인 프로젝트를 이클립스로 생성했다면 아래와 같은 소스를 확인할 수 있습니다. 

package com.nexthops.awesomeweather;

import android.app.Activity;

import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;
import android.widget.ArrayAdapter;
import android.widget.TextView;


public class MainActivity extends Activity
        implements NavigationDrawerFragment.NavigationDrawerCallbacks {

    /**
     * Fragment managing the behaviors, interactions and presentation of the navigation drawer.
     */
    private NavigationDrawerFragment mNavigationDrawerFragment;

    /**
     * Used to store the last screen title. For use in {@link #restoreActionBar()}.
     */
    private CharSequence mTitle;

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

        mNavigationDrawerFragment = (NavigationDrawerFragment)
                getFragmentManager().findFragmentById(R.id.navigation_drawer);
        mTitle = getTitle();

        // Set up the drawer.
        mNavigationDrawerFragment.setUp(
                R.id.navigation_drawer,
                (DrawerLayout) findViewById(R.id.drawer_layout));
    }

    @Override
    public void onNavigationDrawerItemSelected(int position) {
        // update the main content by replacing fragments
        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
                .replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
                .commit();
    }

    public void onSectionAttached(int number) {
        switch (number) {
            case 1:
                mTitle = getString(R.string.title_section1);
                break;
            case 2:
                mTitle = getString(R.string.title_section2);
                break;
            case 3:
                mTitle = getString(R.string.title_section3);
                break;
        }
    }

    public void restoreActionBar() {
        ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setTitle(mTitle);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (!mNavigationDrawerFragment.isDrawerOpen()) {
            // Only show items in the action bar relevant to this screen
            // if the drawer is not showing. Otherwise, let the drawer
            // decide what to show in the action bar.
            getMenuInflater().inflate(R.menu.main, menu);
            restoreActionBar();
            return true;
        }
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {
        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        private static final String ARG_SECTION_NUMBER = "section_number";

        /**
         * Returns a new instance of this fragment for the given section
         * number.
         */
        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            return rootView;
        }

        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            ((MainActivity) activity).onSectionAttached(
                    getArguments().getInt(ARG_SECTION_NUMBER));
        }
    }

}

  어디를 손대야 내가 원하는데로 화면 전환이 될지가 궁금해 질텐데요. Navigation Drawer 프로젝트는 살펴보면 Fragment라는 녀석이 있습니다. 그래서 우리는 Activity를 사용하지 않고 Fragment를 사용할 거에요. 


  보여주고 싶은 화면을 두 개만 만들어 보겠습니다. 우선은 액티비티를 두개 추가해 줍니다. 그리고 소스를 수정할거에요. 

package com.example.awesomenavigation;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class FirstItemFragment extends Activity {

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

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.first_item, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}



  많이 보던 소스입니다. 여기서 아래와 같이 수정 할게요. 

package com.example.awesomenavigation;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FirstItemFragment extends Fragment {

	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		
		View view = inflater.inflate(R.layout.first_item_fragment, container, false);
		
		return view;
	}
}

  Activity를 Fragment로 수정했습니다. 이렇게 더 추가할 화면을 액티비티로 생성한 다음 위와 같이 Fragment로 수정합니다. 다시 메인 액티비티로 넘어가보도록 하겠습니다. 이제 거의 다 됐어요~

package com.example.awesomenavigation;

import android.app.Activity;

import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;
import android.widget.ArrayAdapter;
import android.widget.TextView;


public class MainActivity extends Activity
        implements NavigationDrawerFragment.NavigationDrawerCallbacks {

    /**
     * Fragment managing the behaviors, interactions and presentation of the navigation drawer.
     */
    private NavigationDrawerFragment mNavigationDrawerFragment;

    /**
     * Used to store the last screen title. For use in {@link #restoreActionBar()}.
     */
    private CharSequence mTitle;

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

        mNavigationDrawerFragment = (NavigationDrawerFragment)
                getFragmentManager().findFragmentById(R.id.navigation_drawer);
        mTitle = getTitle();

        // Set up the drawer.
        mNavigationDrawerFragment.setUp(
                R.id.navigation_drawer,
                (DrawerLayout) findViewById(R.id.drawer_layout));
    }

    @Override
    public void onNavigationDrawerItemSelected(int position) {
        // update the main content by replacing fragments
    	Fragment fragment;
    	FragmentManager fragmentManager = getFragmentManager();
    	switch(position) {
    	case 0:
    		fragment = new FirstItemFragment();
    		break;
    		
    	case 1:
    		fragment = new SecondItemFragment();
    		break;
    	default:
    		fragment = new FirstItemFragment();
    	}
    	
    	fragmentManager.beginTransaction()
    	.replace(R.id.container, fragment)
    	.commit();
    }

    public void onSectionAttached(int number) {
        switch (number) {
            case 1:
                mTitle = getString(R.string.title_section1);
                break;
            case 2:
                mTitle = getString(R.string.title_section2);
                break;
            case 3:
                mTitle = getString(R.string.title_section3);
                break;
        }
    }

    public void restoreActionBar() {
        ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setTitle(mTitle);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (!mNavigationDrawerFragment.isDrawerOpen()) {
            // Only show items in the action bar relevant to this screen
            // if the drawer is not showing. Otherwise, let the drawer
            // decide what to show in the action bar.
            getMenuInflater().inflate(R.menu.main, menu);
            restoreActionBar();
            return true;
        }
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {
        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        private static final String ARG_SECTION_NUMBER = "section_number";

        /**
         * Returns a new instance of this fragment for the given section
         * number.
         */
        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            return rootView;
        }

        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            ((MainActivity) activity).onSectionAttached(
                    getArguments().getInt(ARG_SECTION_NUMBER));
        }
    }

}


  50번 째 줄 부터 처음 MainActivity와 변경된 부분을 확인하면 되요. Fragment를 사용하여 Activity위에서 그려지는 부분을 변경하는 거에요. Fragment에 대한 이해가 없는 분이라면 Fragment에 대한 이해부터 하시고 이것을 보시면 더 쉽게 이해할 수 있어요. 

  화면을 꾸며주고 싶은 부분은 Fragment에서 추가하면됩니다. Activity와 생명주기도 조금 다르고, 사용하는 방법도 조금 다르니 꼭 Fragment에 대해 이해하도록 해요~


「 Tip!! 

1. 안드로이드에서 제공하는 기본 Navigation Drawer 프로젝트를 생성한다. 

2. 구성하고 싶은 화면을 Activity로 생성한다. 

3. Activity 를 Fragment 로 변경한다. 

4. MainActivity에서 Fragment를 replace 한다. 그러면 원하는 화면으로 휙휙 전환된다. 

가장 쉬운 방법으로 Mac에 MySQL을 설치해 보도록 하겠습니다. 


우선 MySQL 홈페이지에 가셔서 다음과 같이 진행합니다. 

다운로드 페이지로 이동한 뒤 Conmmunity Edition을 받도록 할게요.



Community Server를 선택하시고 다음 페이지로 넘어가면 사용 중인 OS를 선택하고 해당 OS 버전에 맞는 설치 파일을 다운 받아 설치를 진행하도록 합니다. 


바로 설치해서 사용할 수 있도록 DMG 파일을 다운 받습니다. 

다운로드를 진행할 때 계정이 필요한데 계정 발급은 무료니까 걱정마시고 만들어서 사용하세요. 


파일을 다 다운로드 받았으면 실행을 합니다. 설치 파일을 실행 시키면 아래와 같은 화면을 확인할 수 있습니다. 


그리고 설치를 진행하고, 설치가 완료되면 처음으로 설정된 root의 패스워드가 나옵니다. 창을 그냥 닫지 마시고 꼭!!! 기억해두세요~



설치는 마무리 되었습니다. 그럼 MySQL 서비스를 시작할 일만 남았죠?

아주 아주 아주 쉽습니다. 시스템 환경 설정에 들어가시면 MySQL 아이콘이 있습니다. 실행하도록 합니다. 


Start MySQL Server을 클릭 하시면 바로 시작 됩니다. 

설치 과정은 여기 까지 입니다!

+ Recent posts