Spring Boot 에서 MySQL 테스트 중 한글이 ? 로 깨져서 나오는 것을 확인하였다.

 

아래와 같이 해결 방안을 찾아서 해결 하였다.

 

Docker MySQL 컨테이너 접속

docker exec -it mk_mysql bash

파일 수정을 위해 vim 설치

apt-get update
apt-get install vim

다음의 내용을 파일에 추가

vim /etc/mysql/my.cnf
[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
collation-server = utf8_unicode_ci
init-connect='SET NAMES utf8'
character-set-server = utf8

컨테이너 재실행 후 조회하니 한글이 잘 나온다.

 

 

 


Reference

 

컨테이너 MySQL 한글 인코딩 해결 방법

컨테이너로 기동한 mysql의 database와 table의 한글 깨지는 현상을 해결하기 위한 utf-8 인코딩 방법

velog.io

 

'개발이야기 > Docker' 카테고리의 다른 글

(Mac) Docker 에 MySql 설치하기  (0) 2022.03.06
(Mac) Docker 설치  (0) 2022.03.06

MySQL을 Docker 에 설치하였고, Spring boot 와 연동하기 전 database 를 생성해보자.

 

Docker에 MySQL을 설치하는 방법은 아래 글을 참고!

 

(Mac) Docker 에 MySql 설치하기

1. Docker 공식 홈페이지의 가이드를 확인한다. https://hub.docker.com/_/mysql Mysql - Official Image | Docker Hub We and third parties use cookies or similar technologies ("Cookies") as described belo..

mkdevlab.tistory.com

 

Spring boot 으로 간단한 웹 어플리케이션을 만들 예정이다.

 

CRUD 를 모두 테스트해볼 수 있는 국민 웹앱 프로젝트 '게시판'!

 

그 전에 앞서 프로젝트에서 사용할 database 를 하나 만들어보자.

 

다음과 같이 기존에 만들어 놓은 docker 의 mysql image 를 실행하고 접속해보자.

docker start mk_mysql
...
docker exec it mk_mysql bash
...
mysql -u root -p
...

 

아래 명령어를 실행해서 기존에 어떤 database 가 존재하는 지 확인해보자

show databases;

show databases;

 

프로젝트에 사용 할 database 는 mkdevlab 으로 하도록 하겠다.

CREATE DATABASE mkdevlab;

 

mkdevlab 이라는 database 가 정상적으로 생성된 것을 확인할 수 있다.

 

다음에는 프로젝트에 필요한 table 을 생성해보도록 하겠다.

 

끝.

NAS 초기 설정 중 겪은 어려웠던 내용을 공유해본다.

 

현재 집의 네트워크 구조는 아래와 같다.

인터넷 ---> SK브로드밴드 모뎀 ---> 무선공유기 ---> NAS

 

NAS 설치 후 초기 셋팅을 완료하고 동일한 네트워크 망에서는 잘 접속이 되는 것을 확인하였다.

근데 NAS 를 집에서만 접속해서 사용할 수만은 없는 것이기에.. 외부망에서 접속을 원했다!!

유튜브를 굉장히 많이 검색했고.. 열심히 따라했지만.. 결국 접속이 안됨..ㅜㅜ

결국 SK브로드밴드 모뎀이 문제인가.. 구글링을 시작했고.. 해답(?)을 찾았다.

이것이 정확한 답인지는 모르겠으나, 이렇게 해서 NAS 를 외부에서 접속 가능해졌다.

해답(?)

1. SK브로드밴드 모뎀에 접속한다. (접속 시 사용되는 계정은 모뎀에서 확인 가능)

2. WAN 메뉴로 이동한다.

3. 서비스모드를 NAT --> 브릿지로 변경한다.

 

이 작업만 완료한다고 해서 NAS 와 연결이 되는 것은 아니고, 무선공유기에서 포트포워딩을 해줘야 한다.

포트포워딩 하는 방법은 쉽게 검색해서 찾을 수 있다. 다음에 한번 설정 해둔 포트포워딩을 공유해보도록 하겠다.

또, https 를 사용하는 방법도 있는데 이것도 공유 해보도록 하겠다.

https 를 사용하면 인증서가 필요할텐데 이부분은 어떻게 적용이 되는 것인지도 한번 공유를 해보겠다~

 

끝!

 

Reference : https://badayak.com/3973

Spring Security 를 실습하고 기록해보자.

 

Spring Security 란?

Spring 에서 제공해주는 보안 솔루션이다. 개발자가 직접 보완 관련 코드를 짤 필요없기 때문에 매우 간편하다. Spring Security 에서는 인증(Authentication) 과 권한(Authorization) 기능을 모두 제공하고 있어, 이 개념을 알아야 한다.

 

인증과 권한

인증(Authentication) 과 권한(Authorization) 을 살펴보면, 인증은 '나'='나' 라는 것을 확인하는 절차이고, 권한은 '나' 는 어느범위까지 사용이 가능한 지를 결정하는 것이다.

 

Spring Security 를 사용하는 이유

>>출처<<

 

예제

예제 설명

Spring Security 를 이용하여 간단한 회원가입 / 로그인 기능 구현해보도록 하자. 이 과정에서 Spring Data JPA 도 사용되는데 이 주제는 다음에 다뤄보도록 하자.

 

총 4개의 화면으로 구성된다. + H2 Console 화면 (실제 사용자 정보가 저장되는 지 확인해보자)

  • 로그인 화면
  • 회원가입 화면
  • 사용자 화면
  • Admin 화면

 

총 6개의 package 로 소스코드를 관리한다.

  • config
  • domain
  • repository
  • dto
  • service
  • controller

시작

1. Spring project 를 하나 생성하자.

https://start.spring.io

  • Gradle
  • Spring Boot 2.6.6
  • Java 8
  • Jar

2. 의존성 추가

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  implementation 'org.springframework.boot:spring-boot-starter-security'
  implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
  compileOnly 'org.projectlombok:lombok'
  runtimeOnly 'com.h2database:h2'
  annotationProcessor 'org.projectlombok:lombok'
}

3. domain - Entity 정보 생성

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Getter
public class UserInfo implements UserDetails {
	
	@Id
	@Column(name = "code")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long code;
	
	@Column(name = "email", unique = true)
	private String email;
	
	@Column(name = "password")
	private String password;
	
	@Column(name = "auth")
	private String auth;

	@Builder
	public UserInfo(String email, String password, String auth) {
		this.email = email;
		this.password = password;
		this.auth = auth;
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		
		Set<GrantedAuthority> roles = new HashSet<>();
		for(String role : auth.split("," )) {
			roles.add(new SimpleGrantedAuthority(role));
		}

		return roles;
	}

	@Override
	public String getPassword() {
		// TODO Auto-generated method stub
		return password;
	}

	@Override
	public String getUsername() {
		return email;
	}

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}
	
}

회원가입 시 [이메일, 패스워드, 권한] 이 저장될 것이다. 여러개의 권한을 가질 수 있고, 콤마[,]로 구분되어 저장된다. 로그인이 될 때 권한 리스트를 불러와 Spring Security 가 인식할 것이다.

 

한가지 주목해야 할 점은 UserDetails 라는 인터페이스를 상속받는 것이다. 이것은 Spring 제공 하는 것으로 인증과 권한을 담당한다.

 

 

4. repository 생성

public interface UserRepository extends JpaRepository<UserInfo, Long> {
	
	Optional<UserInfo> findByEmail(String email);

}

Repository 는 interface 로 생성하고 JpaRepository 를 상속 받는다.

 

5. dto 생성

@Getter
@Setter
public class UserInfoDto {
	
	private String email;
	private String password;
	
	private String auth;

}

 

6. service 생성

@RequiredArgsConstructor
@Service
public class UserService implements UserDetailsService {
	
	private final UserRepository userRepository;
	
	public Long save(UserInfoDto userInfoDto) {
				
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
		userInfoDto.setPassword(encoder.encode(userInfoDto.getPassword()));
		
		return userRepository.save(UserInfo.builder()
				.email(userInfoDto.getEmail())
				.password(userInfoDto.getPassword())
				.auth(userInfoDto.getAuth()).build()
				).getCode();
	}

	@Override
	public UserInfo loadUserByUsername(String email) throws UsernameNotFoundException {

		return userRepository.findByEmail(email)
				.orElseThrow(() -> new UsernameNotFoundException(email));
	}
	
}

UserDetailsService 를 상속받고 loadUserByUsername 오버라이드 함수를 사용한다.

 

7. config 생성

@RequiredArgsConstructor
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
	
	private final UserService userService;
	

	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/css/**", "/js/**", "/img/**");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http
			.authorizeRequests()
				.antMatchers("/login", "/signup", "/user").permitAll()
				.antMatchers("/h2-console/**").permitAll()
				.antMatchers("/").hasRole("USER")
				.antMatchers("/admin").hasRole("ADMIN")
				.anyRequest().authenticated()	
			.and()			
            	.csrf()
                	.ignoringAntMatchers("/h2-console/**")
            .and()
            	.headers()
            		.frameOptions().sameOrigin()
			.and()
				.formLogin()
					.loginPage("/login")
					.defaultSuccessUrl("/")
			.and()
				.logout()
					.logoutSuccessUrl("/")
					.invalidateHttpSession(true);
		
		
	}
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
	}
	

}

WebSecurityConfigurerAdapter 를 상속 받는다. 세 개의 configure 메서드는 역할이 각각 다르다.

configure(WebSecurity web)
// -> static 하위 resource 디렉토리를 접근 불가한 리스트에서 제외할 수 있다.

configure(HttpSecurity http)
// -> http 관련 인증 설정을 담당한다.

    antMatchers // 경로 권한 설정을 담당한다.
        permitAll // 누구나 접근 가능
        hasRole // 기재된 권한이 있는 경우 접근 가능
        anyRequest // antMatchers 외 경로
        authenticated // 권한 있으면 접근 가능

    formLogin() // 로그인 설정
        loginPage("/login") // 로그인 화면
        defaultSuccessUrl("/") // 로그인 성공 시 이동할 화면

    logout() // 로그아웃 설정
        logoutSuccessUrl("/") // 로그아웃 시 이동할 화면
        invalidateHttpSession(true); // 로그아웃 시 세션 제거 여부

configure(AuthenticationManagerBuilder auth)
// -> 로그인 인증을 담당하고, 사용자 정보를 불러온다.

8. controller 생성

@RequiredArgsConstructor
@Controller
public class UserController {
	
	private final UserService userService;
	
	@PostMapping("/user")
	public String signup(UserInfoDto userInfoDto) {
		userService.save(userInfoDto);
		return "/login";
	}
	
	@GetMapping("/logout")
	public String logoutPage(HttpServletRequest request, HttpServletResponse response) {
		new SecurityContextLogoutHandler().logout(request, response, SecurityContextHolder.getContext().getAuthentication());
		return "/login";
	}

}

GET 메소드로도 로그아웃이 가능하다. SecurityContextLogoutHandler 을 사용하면 된다.

 

이제 View 화면을 구성해보자.

  • login.html
  • signup.html
  • main.html
  • admin.html

Controller에 등록을 해도 되지만, WebMvcConfigurer 을 사용하여 이동할 화면을 매핑을 해보자.

 

9. MVC config 생성

@Configuration
public class MvcConfig implements WebMvcConfigurer{

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {

		registry.addViewController("/").setViewName("main");
		registry.addViewController("/login").setViewName("login");
	    registry.addViewController("/admin").setViewName("admin");
	    registry.addViewController("/signup").setViewName("signup");
		
	}
}

 

10. View 생성

 

login.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
	<h1>Login</h1> <hr>
    <img src="/img/info.jpeg" />

    <form action="/login" method="POST">
      <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
      email : <input type="text" name="username"> <br>
      password : <input type="password" name="password"> <br>
      <button type="submit">Login</button>
    </form> <br>

    <a href="/signup">Go to join! →</a>

</body>
</html>

 

signup.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>sign up</title>
</head>
<body>
	<h1>Sign Up</h1> <hr>

    <form th:action="@{/user}" method="POST">
      email : <input type="text" name="email"> <br>
      password : <input type="password" name="password"> <br>
      <input type="radio" name="auth" value="ROLE_ADMIN,ROLE_USER"> admin
      <input type="radio" name="auth" value="ROLE_USER" checked="checked"> user <br>
      <button type="submit">Join</button>
    </form> <br>

    <a href="/login">Go to login →</a>

</body>
</html>

 

main.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Main</title>
</head>
<body>
	<h2>회원 전용 페이지</h2>
	ID : <span sec:authentication="name"></span><br>
	소유 권한 : <span sec:authentication="authorities"></span><br>
	
	<form id="logout" action="/logout" method="POST">
		<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
      	<input type="submit" value="로그아웃"/>
	</form>
	

</body>
</html>

 

admin.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2>관리자 전용 페이지</h2>
    ID : <span sec:authentication="name"></span> <br>
    소유 권한 : <span sec:authentication="authorities"></span> <br>

    <form id="logout" action="/logout" method="POST">
      <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
      <input type="submit" value="로그아웃"/>
     </form>
</body>
</html>

 

11. Spring boot 실행

 

localhost:8080/ 접속하면 main.html 로 이동이 될 것이다. 근데 로그인 정보가 없으니 /login 화면으로 이동 됨.

사용자 정보가 없기 때문에 signup 을 해보자.

localhost:8080/signup

admin 을 선택하는 경우 DB 에 ROLE_USER, ROLE_ADMIN 두개가 들어간다.

H2 console 에서 조회해보면 아래와 같이 조회된다.

 

이제 로그인을 해보자.

ADMIN, USER 두 권한이 모두 존재하기 때문에, 회원용, admin용 화면 두 곳 모두 접속이 가능한 것을 볼 수있다.

로그아웃하면 다시 로그인 화면으로 이동한다.

 

다음으로는 사용자로 가입을 하고 화면 접근을 확인해보자.

localhost:8080/admin 으로 접속 시도 시 위와 같이 403 오류가 발생하는 것을 볼 수 있다.

이것으로 실습을 마치자..

 

Reference : https://shinsunyoung.tistory.com/78

'개발이야기 > Spring Boot' 카테고리의 다른 글

Spring Boot JPA 게시판 - 글 등록/수정 (with MySQL)  (0) 2022.04.13
Spring Boot JPA 게시판 - CRUD (with MySQL)  (0) 2022.04.11
ORM  (0) 2022.04.03

 

Spring 은 고전적으로 iBatis 를 사용하였고, iBatis 는 MyBatis로 발전했다.

~2018년 이전에 개발된 Spring 어플리케이션들을 보면 Mapper 방식으로 MyBatis 를 대부분 채택 했을 것이다.

 

요즘 핫하게 많이 들려오는 ORM 이라는 단어가 있다.

ORM : Object Relational Mapping (객체-관계 매핑)

 

사실 조금 생소하긴 하다. 현업에서 사용하지 않다보니 경험해볼 일이 없기도 하고...

인터넷을 검색해보면 찾을 수 있는 개념은,, (출처 : https://gmlwjd9405.github.io/2019/02/01/orm.html)

 

ORM 이란 ?

  • 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑(연결)해주는 것을 말한다.
    - 객체 지향 프로그래밍은 클래스를 사용하고, 관계형 데이터베이스는 테이블을 사용한다.
    - 객체 모델과 관계형 모델 간에 불일치가 존재한다.
  • ORM을 통해 객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여 불일치를 해결한다.
    - 데이터베이스 데이터 <—매핑—> Object 필드
  • 객체를 통해 간접적으로 데이터베이스 데이터를 다룬다.
    - Persistant API라고도 할 수 있다.
    - Ex) JPA, Hibernate 등

ORM 장단점

 

Hibernate 를 사용하게 되면 persist(), merget(), close() 등을 직접 사용하고, 

transation 이 발생할 때 getTransation.begin(), commit() 등으로 관리를 한다.

 

Spring Data JPA 는 개발자가 조금 더 사용하기 편리하게 만들어 놓은 것이다.

Hibernate 를 한번 감싸고 있고, 위와 같은 명령어를 굳이 신경쓰지 않고 개발을 할 수 있도록 해준다.

서울 열린데이터광장에서 발급받은 Open API 인증키로 Android 에서 호출해볼 수 있는 앱을 간단하게 만들어보았다.

이런 꿀 정보를 제공하는 앱들은 이미 많지 않을까라는 생각이 들지만,,

UI 를 이쁘게 만들어서 앱 출시를 해봐도 좋을 것 같다..ㅎㅎ

 

Open API 인증키 발급 정보는 아래 링크에서 확인!

https://mkdevlab.tistory.com/15

 

서울열린데이터 광장 API 인증키 발급

1. 서울 열린데이터 광장 로그인 https://data.seoul.go.kr/index.do 열린데이터광장 메인 데이터분류,데이터검색,데이터활용 data.seoul.go.kr 2. 인증키 정보 발급 받은 인증키는 '서울시 문화 행사 정보' 다.

mkdevlab.tistory.com

 

사용 Tool : Android Studio

1. Android Studio 에서 project 를 하나 생성한다.

OpenApiSample01 로 하나 만듦.

 

2. AndroidManifest.xml 수정

인터넷 사용이 필요하기 때문에 아래 권한을 manifest 하위에 추가 함.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mkdevlab.openapisample01">

    <uses-permission android:name="android.permission.INTERNET"/>

    ...생략...
    
</manifest>

3. 조회 화면 구성

RecyclerView 를 사용하여 간단하게 작성하였다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:layout_editor_absoluteX="1dp"
        tools:layout_editor_absoluteY="1dp"
        tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

아이템이 구성 레이아웃은 다음과 같이 간단하게 작성

<item_event.xml>

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingHorizontal="20dp"
    android:paddingVertical="10dp">

    <TextView
        android:id="@+id/date"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:text="날짜"/>

    <TextView
        android:id="@+id/code_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:text="전시종"/>

    <TextView
        android:id="@+id/event_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/code_name"
        android:paddingVertical="10dp"
        android:text="제목"
        android:lines="1"
        android:textSize="18sp"
        android:textStyle="bold"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_marginTop="10dp"
        android:background="@android:color/darker_gray"
        android:layout_below="@+id/event_title"/>
</RelativeLayout>

아래와 같은 레이아웃이다..

맨 아래 결과를 보면 이해가 더 쉬울 것이다..

 

4. 조회 코드 작성

RecyclerView 를 사용하고 있기 때문에 별도 커스텀 Adapter 를 만들어서 View 에 데이터를 보여주게끔 하였다.

Open API URL 를 조회하는 자바 코드는 다음과 같다.

 

urlAddress 에 사용된 URL 및 KEY 정보는 상수로 만들어져있다.

코드를 작성하면서 return 되는 값을 그때그때 확인하기 위해 log 를 남겨보았다.

public void fetch() {

        Log.d("main", "fetch() started::");

        String urlAddress = URL_1 + KEY + URL_2;

        new Thread() {
            @Override
            public void run() {
                super.run();

                try {
                    URL url = new URL(urlAddress);

                    InputStream is = url.openStream();
                    InputStreamReader isr = new InputStreamReader(is);
                    BufferedReader reader = new BufferedReader(isr);

                    StringBuffer buffer = new StringBuffer();
                    String line = reader.readLine();
                    while(line != null){
                        buffer.append(line + "\n");
                        line = reader.readLine();
                    }
                    String jsonData = buffer.toString();

                    JSONObject obj = new JSONObject(jsonData);

                    JSONObject culturalEventInfo = (JSONObject) obj.get("culturalEventInfo");

                    JSONObject result = (JSONObject) culturalEventInfo.get("RESULT");

                    //Log.d("main","result : " + result);
                    Log.d("main","code : " + result.get("CODE"));
                    Log.d("main","message : " + result.get("MESSAGE"));

                    JSONArray rowArray = culturalEventInfo.getJSONArray("row");

                    Log.d("main", "row : "  + rowArray);

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {

                            try {

                                for (int i = 0; i < rowArray.length(); i++){
                                    JSONObject tempOjb = rowArray.getJSONObject(i);
                                    String codeName = tempOjb.getString("CODENAME");
                                    String title = tempOjb.getString("TITLE");
                                    String place = tempOjb.getString("PLACE");
                                    String date = tempOjb.getString("DATE");
                                    String useTarget = tempOjb.getString("USE_TRGT");
                                    String useFee = tempOjb.getString("USE_FEE");
                                    String orgLink = tempOjb.getString("ORG_LINK");
                                    String mainImg = tempOjb.getString("MAIN_IMG");

                                    //Log.d("main", "row : " + i + ", codeName : " + codeName + ", title : " + title);
                                    EventData eventData = new EventData(codeName, title, place, date, useTarget, useFee, orgLink, mainImg);
                                    list.add(eventData);
                                }

                                eventAdapter.setEventList(list);

                                Log.d("main","list : " + list);

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    });


                } catch (IOException | JSONException e) {
                    e.printStackTrace();
                }

            }
        }.start();

    }

 

5. Emulator 를 실행 후 결과

 

짠! 내가 구성한 item 레이아웃으로 결과가 조회된 것을 볼수 있다.

10개를 조회했기 때문에 아래로 스크롤도 가능하다.

 

이제 각 item을 클릭했을 때 자세한 정보를 보여줄 수 있는 화면을 하나 만들어봐야겠다.

'개발이야기 > Android 개발' 카테고리의 다른 글

클래식 숫자야구 게임  (0) 2022.04.03

1. 서울 열린데이터 광장 로그인

https://data.seoul.go.kr/index.do

 

열린데이터광장 메인

데이터분류,데이터검색,데이터활용

data.seoul.go.kr

2. 인증키 정보

발급 받은 인증키는 '서울시 문화 행사 정보' 다.

http://data.seoul.go.kr/dataList/OA-15486/S/1/datasetView.do

 

서울시 문화행사 정보

서울문화포털에서 제공하는 문화행사 정보입니다. <br />공연, 행사에 대한 장소, 날짜, 기관명, 이용대상, 이용요금, 출연자, 프로그램 등의 정보를 제공합니다.

data.seoul.go.kr

3. 인증키 신청

  • 사용 URL : 티스토리 URL 입력
  • 이메일 : 개인 이메일 입력
  • 활용용도 : 참고자료 조사
  • 내용 : 참고자료 조사

이렇게 입력하고 '인증키 신청' 버튼을 클릭하면 인증키가 즉시 발행된다.

아마, 간단하게 조회해볼 수 있는 API 라서 별도의 심사는 거치지 않는 것으로 보여진다.

 

발급 받은 Key 를 사용하여, 호출 포맷에 맞춰 브라우저에서 호출해보자.

 

4. 브라우저 호출 테스트

URL : http://openapi.seoul.go.kr:8088/<인증키>/json/culturalEventInfo/1/5/

 

원하는 포맷으로 return 받을 수 있다. 위에 페이지 및 조회 건수를 설정하여 호출도 가능하다.

아래 표 참고!

출처: 서울열린데이터광장

5. 호출 결과

{
  "culturalEventInfo": {
    "list_total_count": 2554,
    "RESULT": {
      "CODE": "INFO-000",
      "MESSAGE": "정상 처리되었습니다"
    },
    "row": [
      {
        "CODENAME": "연극",
        "TITLE": "이야기극장 100년의 동요",
        "DATE": "2022-06-25~2022-06-26",
        "PLACE": "노원어린이극장",
        "ORG_NAME": "노원문화재단",
        "USE_TRGT": "24개월 이상",
        "USE_FEE": "전석 2만원",
        "PLAYER": "",
        "PROGRAM": "",
        "ETC_DESC": "",
        "ORG_LINK": "https://www.nowonarts.kr/html/pe/performancedetail.php?param_url=https%3A%2F%2Fwww.nowonarts.kr%2Fhtml%2Fpe%2Fperformanceinfo.php%3F%26cate%3D%26skin%3D%26search_state%3D1%26search_syear%3D%26search_smonth%3D%26search_eyear%3D%26search_emonth%3D%26search_order%3D%26search_startPage%3D&idx=254",
        "MAIN_IMG": "https://culture.seoul.go.kr/cmmn/file/getImage.do?atchFileId=1b1b2570b32e4d25bee20cfdd51c005a&thumb=Y",
        "RGSTDATE": "2022-03-29",
        "TICKET": "기관",
        "STRTDATE": "2022-06-25 00:00:00.0",
        "END_DATE": "2022-06-26 00:00:00.0",
        "THEMECODE": ""
      },
      {
        "CODENAME": "콘서트",
        "TITLE": "2022 한국가곡 세기의 콘서트 #2. 향수",
        "DATE": "2022-05-20~2022-05-20",
        "PLACE": "플레이맥",
        "ORG_NAME": "마포문화센터",
        "USE_TRGT": "8세이상 관람가",
        "USE_FEE": "전석 30,000원",
        "PLAYER": "",
        "PROGRAM": "",
        "ETC_DESC": "",
        "ORG_LINK": "https://www.mfac.or.kr/performance/whole_view.jsp?sc_b_category=17&sc_b_code=BOARD_1207683401&pk_seq=2004&page=1",
        "MAIN_IMG": "https://culture.seoul.go.kr/cmmn/file/getImage.do?atchFileId=e2cd064a6b1048a88a7426bea8d6ca9f&thumb=Y",
        "RGSTDATE": "2022-03-31",
        "TICKET": "기관",
        "STRTDATE": "2022-05-20 00:00:00.0",
        "END_DATE": "2022-05-20 00:00:00.0",
        "THEMECODE": ""
      },
      {
        "CODENAME": "클래식",
        "TITLE": "김선욱 피아노 리사이틀 M소나타 시리즈 1",
        "DATE": "2022-05-18~2022-05-18",
        "PLACE": "마포아트센터 아트홀맥",
        "ORG_NAME": "",
        "USE_TRGT": "8세이상 입장가능(미취학아동입장불가)",
        "USE_FEE": "VIP 60,000원,R석 50,000원, S석 30,000원",
        "PLAYER": "출연 : 피아니스트 김선욱",
        "PROGRAM": "□ 프로그램 슈베르트 네 개의 즉흥곡, D. 899 (Op. 90) 알베니즈 ‘이베리아’ 모음곡 2권 리스트 피아노 소나타 b단조, S. 178",
        "ETC_DESC": "",
        "ORG_LINK": "https://www.mfac.or.kr/performance/whole_view.jsp?sc_b_category=17&sc_b_code=BOARD_1207683401&pk_seq=2001&page=1",
        "MAIN_IMG": "https://culture.seoul.go.kr/cmmn/file/getImage.do?atchFileId=7f2ab2cb8441407e9c78b8b388efc284&thumb=Y",
        "RGSTDATE": "2022-03-18",
        "TICKET": "기관",
        "STRTDATE": "2022-05-18 00:00:00.0",
        "END_DATE": "2022-05-18 00:00:00.0",
        "THEMECODE": "기타"
      },
      {
        "CODENAME": "문화교양/강좌",
        "TITLE": "양재도서관 인문프로그램 「문화공감, 잇-다」- 2022년 세대를 잇는 리버스 멘토링",
        "DATE": "2022-05-12~2022-08-11",
        "PLACE": "서초구립양재도서관",
        "ORG_NAME": "서초구립양재도서관",
        "USE_TRGT": "관심 있는 시민 누구나(성인)",
        "USE_FEE": "",
        "PLAYER": "1강 : 송주희, 김윤철 / 2강 : 배윤슬 / 3강 : 이슬아 / 4강 : 유네린",
        "PROGRAM": "양재도서관 인문프로그램 「문화공감, 잇-다」는 매년 다방면의 주제로 시대를 관통하는 새로운 시각을 제시합니다.  2022년은 '리버스 멘토링'을 콘셉트로 MZ세대 인플루언서와 만남을 통해 세대 간 소통을 시도하고 다양한 삶의 가치를 공유합니다. ",
        "ETC_DESC": "코로나-19 상황에 따라 비대면 전환 운영 가능합니다. ",
        "ORG_LINK": "https://yangjae.seocholib.or.kr/NoticeInfoDetail/11513",
        "MAIN_IMG": "https://culture.seoul.go.kr/cmmn/file/getImage.do?atchFileId=a7df7232e29d4e8c8b0d5bd32b6bc107&thumb=Y",
        "RGSTDATE": "2022-03-27",
        "TICKET": "기관",
        "STRTDATE": "2022-05-12 00:00:00.0",
        "END_DATE": "2022-08-11 00:00:00.0",
        "THEMECODE": ""
      },
      {
        "CODENAME": "콘서트",
        "TITLE": "김창완밴드콘서트",
        "DATE": "2022-05-12~2022-05-12",
        "PLACE": "노원문화예술회관 대공연장",
        "ORG_NAME": "노원문화재단",
        "USE_TRGT": "8세 이상",
        "USE_FEE": "R석 6만원 / A석 4만원",
        "PLAYER": "",
        "PROGRAM": "",
        "ETC_DESC": "",
        "ORG_LINK": "https://www.nowonarts.kr/html/pe/performancedetail.php?param_url=https%3A%2F%2Fwww.nowonarts.kr%2Fhtml%2Fpe%2Fperformanceinfo.php%3F%26cate%3D%26skin%3D%26search_state%3D1%26search_syear%3D%26search_smonth%3D%26search_eyear%3D%26search_emonth%3D%26search_order%3D%26search_startPage%3D&idx=255",
        "MAIN_IMG": "https://culture.seoul.go.kr/cmmn/file/getImage.do?atchFileId=bb6cde1ab10149d48feaa19cd343059c&thumb=Y",
        "RGSTDATE": "2022-03-29",
        "TICKET": "기관",
        "STRTDATE": "2022-05-12 00:00:00.0",
        "END_DATE": "2022-05-12 00:00:00.0",
        "THEMECODE": ""
      }
    ]
  }
}

 

다음으로는 이 Open API URL 을 Android 와 접목하여 앱으로 간단하게 만들어본 결과를 공유해봐야겠다 !!

친구랑 수첩에 적어서 플레이하던 추억 속의.. 숫자야구를 앱으로 만들어봤다.

(1년이 넘었지만...)

 

3 스트라이크가 정석이지만, 재미를 위해 4, 5 를 추가 했다..

 

게임 기록은 안드로이드에서 가볍게 사용할 수 있는 내부 DB.. SQLite 를 사용 함.

 

소스 코드는 다음에 기회가 되면 github에 올려야지~

https://play.google.com/store/apps/details?id=play.classic.baseballgameapp 

 

숫자야구 - Google Play 앱

숫자야구 게임

play.google.com

 

 

 

생각 날 때마다 commit 하는,, 볼 것 없는...

 

https://github.com/mkChung924

 

mkChung924 - Overview

mkChung924 has 11 repositories available. Follow their code on GitHub.

github.com

 

 

 

 

* 광고 아님.

* 내돈내산임.

 

휴대폰 사진이 늘어나면서 저장공간이 부족해.. Synology NAS 를 구매 함.

개인용으로 사용하기 좋다는 모델로 구매 함. 사이즈 딱 좋음.

  • NAS 모델 : 시놀로지 NAS 나스 DS220+ 
  • 하드 : 씨게이트 아이언울프 HDD 4TB (2개) --> Raid 로 사용하려고 2개 구매 함.
  • 구매일자: 2022년 2월 25일

구매하고 며칠 동안 설정한 내역들을 정리해보겠다. (사전에 유튜브 찾아보고.. 구글링을 많이 했었음..)

 

<사용 중인 인터넷 및 공유기 정보>

  • 인터넷: SK 브로드밴드
  • 공유기 : Netis

+ Recent posts