서블릿은 Core J2EE Patterns 에서 정이한 Front Controller 패턴을 따른다. 

Front Controller 

  • 중복적인 제어 로직을 피할 수 있다. 
  • 여러 요청에 공통인 로직을 적용할 수 있다.
  • 뷰에서 시스템 로직을 분리할 수 있다. 
  • 시스템의 제어 접속점을 중앙 집중화 할 수 있다. 

HTTP 요청에 대응하여 javax.servlet.http.Httpservlet 클래스가 GenericServlet 클래스로 부터 상속되어 HTTP 요청에 대해서만 반응하는 service() 메서드가 구현되어 있다. 또한, 다음과 같은 각 HTTP 메서드 유형에 대응하는 비어있는 메서드 구현을 제공한다. 


 HTTP 메서드

서블릿 메서드 

목적 

GET 

doGet() 

지정된 URL에서 리소스를 가져온다  

HEAD

doHead() 

GET과 동일하지만, 헤더만 반환된다 

POST 

doPost() 

일반적으로 웹 폼 제출 시에 사용된다  

PUT 

doPut() 

URL에 제공된 엔터티를 저장한다. 

DELETE 

doDelete() 

URL로 식별된 리소스를 삭제한다.  

OPTIONS 

doOptions() 

어떤 HTTP 메서드가 허용되는지를 반환한다. 

Trace 

doTrace() 

진단 목적으로 사용된다. 


현재 Java EE7 에서는 서블릿 프로토콜이 HTTP만 지원하므로 서블릿 클래스가 HttpServlet 클래스에서 파생하도록 하면 된다. 


Spring AOP

소프트웨어를 개발할때마다 공통적으로 부딪히는 문제들이 아래와 같이 있다. 

  • 로깅
  • 보안/인증
  • 트랜잭션
  • 리소스 풀링
  • 에러 검사
  • 정책 적용
  • 멀티 쓰레드 안전 관리 
  • 데이터 퍼시스턴스 

클래스 또는 컴포넌트로 모듈화하지만 문제들을 어떻게 해결하는것은 또 다른 문제 
일반적으로 문제영역(Problem Domain)은 핵심 관심과 횡단 관심으로 구성된다.

핵심 관심

 관심

패러다임 

모듈 

핵심관심 

OOP 

클래스/컴포넌트/서비스 

횡단관심 

AOP 

 관점

관점지향 프로그램은 관심의 분리를 통해 문제 영역을 핵심 관심과 횡단관심의 독립적인 모듈로 분해하는 프로그래밍 패러다임으로 다음과 같은 이점을 제공한다

  • 관심의 분리 도출
  • 비즈니스 로직 이해도 향상
  • 생산성 향상
  • 비즈니스 코드와 횡단 관심사들 간의 결합성 제거
  • 비즈니스 코드 재사용성 향상
  • 확장 용이
업무 기능과 시스템 기능을 분리하여 코드를 작성함으로써 서로 다른 관심사를 분리할 수 있으며, 업무 로직을 쉽게 이해할 수 있게 됨으로써 생산성의 향상을 가져올 수 있다.  또한 이처럼 업무 기능을 구현하는 코드와 시스템 기능을 구현하는 횡단 관심사들 간의 결합성이 제거되어 업무코드를 재사용하고 확장하기 쉽다

* 관점지향 용어 

 용어

설명 

어드바이스 

관점이 언제, 무엇을 하는지를 정의함. 

조인포인트 

관점이 플러그인되는 애플리케이션의 실행 위치 

포인트컷 

관점이 어드바이스하는 위치(어디서). 조인포인트의 범위를 축소함 

관점 

어드바이스와 포인트컷 결합. 무엇을 언제, 어디서 하는지를 정의함  

엮기 

관점을 대상 객체에 적용시키는 것 프록시 객체 생성 

도입 

기존 클래스에 새로운 메서드가 애트리뷰트를 추가하는것 


튜토리얼스 포인트 부연설명

AOP Terminologies:

Before we start working with AOP, let us become familiar with the AOP concepts and terminology. These terms are not specific to Spring, rather they are related to AOP.

TermsDescription
AspectA module which has a set of APIs providing cross-cutting requirements. For example, a logging module would be called AOP aspect for logging. An application can have any number of aspects depending on the requirement.
Join pointThis represents a point in your application where you can plug-in AOP aspect. You can also say, it is the actual place in the application where an action will be taken using Spring AOP framework.
AdviceThis is the actual action to be taken either before or after the method execution. This is actual piece of code that is invoked during program execution by Spring AOP framework.
PointcutThis is a set of one or more joinpoints where an advice should be executed. You can specify pointcuts using expressions or patterns as we will see in our AOP examples.
IntroductionAn introduction allows you to add new methods or attributes to existing classes.
Target objectThe object being advised by one or more aspects, this object will always be a proxied object. Also referred to as the advised object.
WeavingWeaving is the process of linking aspects with other application types or objects to create an advised object. This can be done at compile time, load time, or at runtime.

아래 그림 설명


프로그램이 실행 과정 속에 여러 개의 조인포인트를 정의할수 있으며, 이둘중 어느위치에 포인트 컷을 지정하여 그 위치에서 어드바이스가 실행되도록 설정한다.


어드바이스란 관점의 실제 구현체로 포인터컷에 삽입되어 동작할 수 있는 코드로서, 관점이 무엇을 언제하는지 정의  


어드바이스 

설명 

before 

메서드가 호출되기 전에 어드바이스 기능이 발생함 

after 

메서드의 실행이 완료된 후 결과와 관계없이 어드바이스 기능이 발생함 

after-returning 

메서드의 실행이 성공적으로 완료된 후 어드바이스 기능이 발생함

after-throwing 

 메서드가 예외를 던진 후에 어드바이스 기능이 발생함 

around 

 메서드가 호출되기 전과 후에 어드바이스 기능이 발생함


조인포인트는 관점이 플러그인 되는 애플리케이션의 실행위치 즉, 관점의 코드가 애플리케이션의 정상적인 흐름 속에 삽입되어 새로운 행위를 추가하는 시점이다. 

  • 호출되는 메서드
  • 예외가 던져지는 위치
  • 필드 값이 수정될때 
관점이 모든 조인 포인트를 어드바이스 할 필요가 없기 때문에 포인트 컷으로 관점이 어드바이스하는 조인포인트의 범위를 축소시킨다. 
* 어드바이스가 관점의 언제 무엇을 하는지를 정의한다면, 포인트컷은 어디서를 정의한다. 포인트컷은 명확한 클래스와 메서드 이름 

Spring AOP 구현 

Spring AOP 설정 

설정을 위해서는 두 개의 모듈을 필요로 한다

  • spring-aop.jar
  • spring-aspects.jar
Maven을 사용하므로 pom.xml 파일에 종속성을 추가하면 된다. 

예제로 로깅 기능을 제공하는 관점을 구현해보다. 각 메서드가 호출될 때 해당 메서드가 호출되었다는 것을 로깅하는 코드를 작성하는 것은 전혀 어렵지는 않지만, 번거로우 일이다.
Spring AOP를 사용하면 쉽게 구현할 수 있고, 또한, 메서드가 추가될 때마다 로기 기능을 구현하지 않아도 새로운 메서드에 대한 로깅 기능을 사용할 수 있다.

- 관점 클래스 정의
Spring AOP는 관점을 자바 클래스로 정의하고, 어드바이스를 관점 클래스의 메서드로 구현한다.





의존성을 주입할 수 있는 대상은 다른 Spring 빈뿐만이 아니다

의존성을 주입할 수 있는 대상은 아래와 같다

  • 다른 Spring 빈 참조
  • 단순 값
  • 컬렉션
  • 널(null)
  • SpEL 표현식
다른 의존성 주입 대상들에 대해 사용하는 방법


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class CustomerRepositoryImpl implements CustomerRepository {
    
    private String driverClassName;
    private String url;
    private String username;
    private String password;
 
    public String getDriverClassName() {
        return driverClassName;
    }
 
    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }
 
    public String getUrl() {
        return url;
    }
 
    public void setUrl(String url) {
        this.url = url;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getPassword() {
        return password;
    }
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="hello" class="springHello.Hello"/>
    <bean id="customerService" class="service.CustomerServiceImpl">
        <constructor-arg ref="customerRepository"/>
    </bean>
    
    <bean id="customerRepository" class="repository.CustomerRepositoryImpl">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/order_system"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
</beans>
 
cs

아래 xml 코드의 customerRepository 부분의 property 값들과 같이 스프링 빈이 아니어도 의존성 주입을 할수 있다

* 예제용으로 보여주었을 뿐 데이터 베이스를 구현하기 위한 더좋은 방법이 있으므로 이방법으로 할필요없는 없다

단일 필드 값만이 아니라 컬렉션 타입의 필드에도 값을 주입할 수 있다. 


 필드 타입

요소 

설명 

Collection/List 

<list> 

중복 허용 값 목록 

Collection/Set 

<set> 

중복되지 않는 값 목록 

Map  

<map> 

어떤 타입이든 허용되는 이름 값 컬렉션 

Properties 

<props> 

Sring 타입의 이름-값 컬렉션 

앞의 데이터베이스 연결 정보를 컬렉션 타입으로 정의할 때 가장 적당한 타입은 Properties 타입니다. 우리는 다음과 같이 Properties 타입의 컬렉션 필드를 정의할 수 있다. 

1
2
3
4
5
6
public class CustomerRepositoryImpl implements CustomerRepository {
    private Properties properties;
    
    public void setProperties(Properties properties){
        this.properties = properties;
    }
cs

1
2
3
4
5
6
7
8
9
10
    <bean id="customerRepository" class="repository.CustomerRepositoryImpl">
        <property name="properties">
            <props>
                <prop key="driverClassName">com.mysql.jdbc.Driver</prop>
                <prop key="url">jdbc:mysql://localhost:3306/order_system</prop>
                <prop key="username">root</prop>
                <prop key="password"></prop>
            </props>
        </property>
    </bean>
cs

Properties 값을 이용할 경우 위와 같이의존성 주입을 할수 있다. 

키값을 불러오는 형식으로 아래와 같이 사용가능하다. 

1
2
3
4
private String driverClassName = properties.getProperty("driverClassName");
private String url = properties.getProperty("url");
private String username = properties.getProperty("username");
private String password = properties.getProperty("password");
cs

이외에도 

Map, List 를 이용하는 방법들이 있으니 교재를 참조 (p119 - p 121)

필드에 null값을 주입해야 하는 경우에는 다음과 같이 <null/> 요소를 사용한다.

1
<property name="nullableProparty"><null/></property>
cs

또한 Spring 표현식 언어인 SpEL(Sproing Expression Language)의 #{} 표현식을 사용하여 필드값을 주입할 수 있다. 

1
2
3
<bean>
    <property name="property" value="#{5}" />
</bean>
cs

Spring 표현식을 이용할수 있는 값들에 대해서는 교제 p122 에 있는 표를 참조하면 된다. 


3.5 어노테이션 

XML 설정 최소화

XML 설정 파일을 사용할 때 순수하가 POJO로 Spring 빈을 수현할 수 있으며, 앞에서 살펴본 바와 같이 소스 코드를 변경시키지 않고도 설정을 변경시키는 것만으로도 유지 보수를 쉽게 할수 있게 한다는 이점을 갖게된다 

그러나 Java 코드와 설정이 분리되어 프로그램의 이해를 어렵게 하고 Spring 빈 클래스가 많아지면 XML 설정도 많아져서 개발자들이 어플리케이션을 개발하는데 어려움을 겪게되는것도 사실이다. 

이런 문제를 해결하기 위해 아래와 같은 두가지 해결책이 있다. 

  • 자동 와이어링
  • 어노테이션 와이어링
자동와이어링은 XML설정 파일은 그대로 사용하면 Spring  빈 설정을 최소한으로 할 수 있ㄷ도록 하는 기능 

어노테이션 와이어링은 XML 설정 파일을 아예 사용하거지 않거나또는, 가능한 사용을 억제하고 어노테이션을 사용하여 Spring 빈을 설정할 수 있도록 하는 기능을 제공 

여기서 와이어링이란 의존성 주입을 통해 Spring 빈을 연결하는 것을 말한다. 

Spring 프레임워크는 다음과 같은 4가지 자동 와이어링 방식을 제공

 방식

autowire 애트리뷰트 

설명 

 이름

 byName

필드와 같은 이름 (또는ID)를 가지는 빈과 자동 와이어링

 타입

 byType

필드와 같은 타입의 빈과 자동 와이어링 

 생성자

contructor 

생성자 매개 변수의 타입과 일치하는 빈을 자동 와이어링1 

자동탐색 

 autodetect

먼저 생성자 자동 와이어링이 수행되고 실패하면 타입 와이어링이 수행됨  

다음과 같이 Bean 클래스가 정의되어 있다고 하자 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Bean1 {}
public class Bean2  {
    private Bean1 bean1;
    public void setBean1(Bean1 bean){
        this.bean1 = bean1
    }
}
public class Bean3 {
    private Bean1 bean1;
    public Bean3(Bean1 bean1){
        this.bean1 = bean1;
    }
}
 
<bean id="bean1" class="Bean1"/>
cs

byName 이 지정되면 필드명과 같은 이름을 가지는 빈을 찾아 자동으로 와이어링 시킨다

-> 이름 와이어링

아래는 Bean2 크래스의 필드명이 bean1이므로 빈 이름이 bean1인 빈을 찾아서 필드에 의존성을 주입한다. 

1
<bean id="bean2" class="Bean2" autowire="byName"/>
cs


byType 이 지정되면 필드와 타입의을 가지는 빈을 찾아 자동으로 와이어링 시킨다

-> 타입 와이어링

아래는 Bean2 클래스의 bean1필드의 타입이 Bean1 클래스이므로 class 애트리뷰트가 Bean1인 bean1 빈을 찾아서 필드에 의존성을 주입시킨다. 

1
<bean id="bean2" class="Bean2" autowire="byType"/>
cs



1
<bean id="bean3" class="Bean3" autowire="constructor"/>
cs



어노태이션 와이어링
XML 설정을 최소하 하기 위해 Spring 프레임 워크는 빈 와이어링 즉, 의존성 주입을 지원하는 어노테이션을 제공한다. 이러한 기능을 어노테이션 와이어링이라고 한다. 어노테이션 와이어링을 사용하기 위해서즌 먼저 XML 설정 파일에 context 네임스페이스를 추가해야 한다. 다음과 같이 <beans> 태그 안에 context 네임스페이스를 추가한다. 
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.1.xsd">
    
<context:annotation-config/>
</beans>
cs

추가로 <context:annotation-config/>도 설정해 어노테이션 와이어링 사용할수 있게 한다. 

어노테이션을 사용한 의존성 주입에 사용할 수 있는 어노테이션은 다음과 같다. 


 어노테이션

제공자 

@Autowired 

Spring 

@Inject 

JSR-330 

@Resource 

JSR-250 

@Autowired 어노테이션은 Spring 프레임워크에서 제공하는 기본적인 어노테이션이다. 이 어노테이션은 필드 또는 생성자, setter 메서드에 적용할 수 있으며, 먼저 타입 와이어링을 시도한 후에 실패하면 이름 와이어링으로 후보 빈을 찾는다.

보통 아래와 같이 오토와이

1
2
3
4
public class CustomerServiceImpl implements CustomerService {    
    @Autowired
    private CustomerRepository repository;
}
cs

필드외에도 생성자와 setter 메서드에 @Autowired 애트리뷰트를 지정할 수도 있다. 

이제 XML 설정 파일에 의존성 주입을 하기 위한 설정을 생략해도 된다. 
만약 와이어링할 빈이 없는 경우 NoSuchBeanDefinitionException이 발생 
이떄 우리는 다음과 같은 두가지 방법을 사용할 수 있다. 

 방안

설정 

선택적 자동 와이어링 지정(null값 허용) 

@Autowired(required=false) 

 와이어링할 빈 지정(범위 한정)

@Qualifier("beanID") 


첫번째 방법은 와이어링을 반드시 해야 하는것은 아니라고 선택적 자동 와이어링을 지정하는것이다. 만약 자동 와이어링할 빈을 찾지 못한다면, 해당 필드에 저장되는 값은 null이 된다. 

1
2
3
import org.springframework.beans.factory.annotation.Autowired;
@Autowired(required=false)
private CustomerRepository repository;
cs

또 다른 방법은 와이어링할 빈을 지정하는 것. 달리말하면, 찾을 수 있도록 범위를 한정하는 것.ㅣ @Qualifier 어노테이션의 괄호 안에 와이어링할 빈 이름을 지정한다.

1
2
3
import org.springframework.beans.factory.annotation.Qualifier;
@Qualifier("customerRepository")
private CustomerRepository repository;
cs


Spring 프레임워크가 제공하는 @Autowired 어노테이션 외에도 Java의 JSR-330사양에 있는 @Inject 어노테이션을 사용할 수 있다. JSR-330 사양은 Java 언어가 제공하는 의존성 주입을 표준 어노테이션이 정의되어 있다. 이들 어노테이션을 사용하려면 클래스 경로에 표준 어노테이션을 구현한 jar 파일이 있어야 한다 . 우리는 Maven을 사용하므로 pom.xml 파일에 종종석을 추가하면 된다 .

1
2
3
4
5
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
cs


자동 빈 발견 

XML 설정을 사용하지 않용하지 않고도 오노테이션을 사용하여 Spring 빈을 설정 할수 있다. 


 어노테이션

설명 

@Component 

클래스가 Spring 빈임을 표시하는 범용 어노테이션 

@Service 

클래스가 서비스를 정의하고 있으ㅁ을 표시하는 @Componenet 

@Repository 

클래스가 데이터 레파지토리를 정의하고 있음을 표시하는 @Component 

@Controller 

 클래스가 Spring MVC 컨트롤러를 정의하고 있음을 표시하는 @Component

@Component 어노테이션은 가장 범용적인 Spring 빈 설정 어노테이션이다. 다음과 같이 Spring 빈 클래스에 지정하면 된다. 이때 디폴트 Spring 빈의 이름은 클래스명의 첫 문자를 소문자로 바꾼 이름이 된다. 아래 예의 Spring 빈은 customerRepositoryImpl 이된다 

1
2
3
@Component
public class CustomerRepositoryImpl implements CustomerRepository {
}
cs

Spring 빈의 이름을 변경하고 싶다면 괄호안에 이름을 지정하면 된다

1
2
3
@Component("customerService")
public class CustomerRepositoryImpl implements CustomerRepository {
}
cs

@Component 어노테이션 대신에 Spring 빈의 성격에 따라 @Service, @Repository, @Controller 어노테이션을 사용할 수 있다. @Service 어노테이션은 Spring 빈이 업무 로직을 구현한 서비스임을 나타낸다. 

1
2
3
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
}
cs

@Repository  어노테이션은 Spring 빈이 데이터 액세서 기능을 제공하는 레파지토리임을 나타낸다. 

1
2
3
@Repository("customerRepository")
public class CustomerRepositoryImpl implements CustomerRepository {
}
cs

@Controller 어노테이션은 Spring MVC에서 컨트롤러임을 나타낸다. 

이렇게 어노테이션으로 정의한 Spring 빈을 자동으로 발견할 수 있도록 XML 설정 파일에 설정해야만 한다. 이때 XML 설정 파일에는 <context:annotation-config/> 대신에 자동으로 Spring 빈을 발견하기 위한 설정만 추가하면 된다 

1
<context:component-scan base-package="com.ensoa.order"/>
cs

base-package애트리뷰트에 지정된 패키지오 서브 패키지를 스캐닝 하여 Spring 빈을 찾아 인스턴스를 생성하고 IoC컨테이ㅅ너 즉, 어플리케이션 컨텍스트에 자동으로 등록된다. 편리하지만 POJO코드보다는 조금 복잡해진다. 그래도 직관적으로 이해할 수 있으므로 아주 유용한 기능이다.

** 중대형 프로젝트는 수십명의 작업자가 같이 작업을 하므로 자동 어노테이션보다는 기존 XML방식으로 의존성을 서술한 방식을 주로 이용한다. XML 방식이 전체적인 의존성 주입방식 파악에 훨씬 유용하기 때문이다

'Java > Spring Framework' 카테고리의 다른 글

스프링 프레임워크 마이그레이션 3.2.9 to 4.3.5  (0) 2016.12.22
Spring 한글설정  (0) 2015.03.19
Spring AOP  (0) 2015.01.19
스프링 학습 개발 도구 설치  (0) 2015.01.13
프레임 워크 배우기 좋은 웹사이트  (0) 2014.11.04

객체지향 자바스크립트

자바스크립트 마스터북

프로그래머는 좋은 프로젝트를 맡는것이 중요
웹은 신한 금융쪽


캐러솔 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 
Ext.application({
    name:'Myapp',
    launch:function(){
        
        var carousel = Ext.create('Ext.Carousel', {
            direction:'vertical',
            indicator:true,
            items:[
                   {
                       title:'Tab 1',
                       html:'<div align="center"><br><img src="../image/j.jpg" width="101" height="133"><br>직급: 왕자</div>',
                       
                   },
                   {
                       title:'Tab 2',
                       html:'<div align="center"><br><img src="../image/k.jpg" width="101" height="133"><br>직급: 킹</div>',
                       
                   },
                   {
                       title:'Tab 3',
                       html:'<div align="center"><br><img src="../image/q.jpg" width="101" height="133"><br>직급: 여왕</div>',
                       
                   }
                   ]
        });
        
        var rootPanel = Ext.create('Ext.Panel', {
            
            fullscreen:true,
            layout:{
                type:'vbox', align:'stretch', pack    :'center'
            },
            defaults:{
                flex:1
            },
            items:[carousel]
                 
        });
        
 
    }
});
 
 
cs


리스트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
 
Ext.application({
    name:'Myapp',
    launch:function(){
        
        
        // 모델에서 필드 정의 
        Ext.define('Contact', {
            extend:'Ext.data.Model',
            config:{
                fields:['firstName''lastName''tel''email']
            }
        });
        
        //필드에 매핑되는 데이터를 정의 
        var contactStore = Ext.create('Ext.data.Store', {
            model:'Contact',
            data:[
                  {firstName:'말자', lastName:'김', tel:'010-1234-1234', email:'user1@naver.com'},
                  {firstName:'태희', lastName:'이', tel:'010-1234-1234', email:'user1@naver.com'},
                  {firstName:'동일', lastName:'박', tel:'010-1234-1234', email:'user1@naver.com'},
                  {firstName:'수길', lastName:'신', tel:'010-1234-1234', email:'user1@naver.com'},
                  {firstName:'민규', lastName:'이', tel:'010-1234-1234', email:'user1@naver.com'},
                  {firstName:'희원', lastName:'박', tel:'010-1234-1234', email:'user1@naver.com'}                  
                  ]
        });
        
        //목록 처리
        var contactList = Ext.create('Ext.dataview.List',{
            store:contactStore,
            itemTpl:'<div>{lastName} {firstName}</div>'
        });
        
        var btnPrev = Ext.create('Ext.Button', {
            text:'이전',
            ui:'back',
            align:'left',
            hidden:true
        });
        
        var navBar = Ext.create('Ext.TitleBar', {
            docked:'top',
            ui:'light',
            title:'연락처 목록',
            items:[btnPrev]
        });
        
        var rootPanel = Ext.create('Ext.Panel', {            
            fullscreen:true,
            layout:'card',
            items: [navBar, contactList],
            items:[navBar, contactList]
                 
        });
        
 
    }
});
 
 
cs


탭바 

카드레이아웃과 달리 트랜지션 효과가 생긴다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Ext.application({
    name:'Myapp',
    launch:function(){
        
        //tab 패널은 자동적으로 버튼이 생긴다
        var homeView = Ext.create('Ext.Panel',{
            title:'Home',
            style:'background-color:#ff0000',
            
        });
 
        var loginView = Ext.create('Ext.Panel',{
            title:'Login',
            style:'background-color:#00ff00',
            
        });
        
        var listView = Ext.create('Ext.Panel',{
            title:'Home',
            style:'background-color:#0000ff',
            
        });
        
        var rootPanel = Ext.create('Ext.tab.Panel', {
            
            fullscreen:true,
            ui:'light',            
            tabBarPosition:'top'//tab의 위치 지정
            items:[homeView, loginView, listView]
                 
        });
        
 
    }
});
 
 
cs

화면출력



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
 
Ext.application({
    name:'Myapp',
    launch:function(){
        
        var homeView = Ext.create('Ext.Panel',{
            title:'Home',
            style:'background-color:#ff0000',
            
        });
 
        var loginView = Ext.create('Ext.Panel',{
            title:'Login',
            style:'background-color:#00ff00',
            
        });
        
        var listView = Ext.create('Ext.Panel',{
            title:'List',
            style:'background-color:#0000ff',
            
        });    
        
        var btnHomeView = Ext.create('Ext.Button', {
            text:'Home',
            ui:'back',
            align:'left',
            hidden:true,
            handler:function(btn,event){
                //화면 전환 transition 화면 전환 효과
                rootPanel.getLayout().setAnimation({
                    type:'slide',
                    direction:'right',
                    
                });
                
                // 화면 지정
                rootPanel.setActiveItem(homeView);
                
                //Titlebar의 제목을 HomeView로 셋팅 
                navBar.setTitle('HomeView');
                btnHomeView.hide (); //화면이 HomeView이면 Home 버튼은 숨김                
                btnLoginViewRight.show();
                btnLoginViewLeft.hide();
                btnListView.hide();
            }
            
        });
        
        var btnLoginViewRight = Ext.create('Ext.Button', {
            text:'Login',
            ui:'forward'// 버튼 정류
            align:'right',
            handler:function(btn,event){
                //화면 전환 transition 화면 전환 효과
                rootPanel.getLayout().setAnimation({
                    type:'slide',
                    direction:'left',
                    
                });
                
                // 화면 지정
                rootPanel.setActiveItem(loginView);
                
                //Titlebar의 제목을 HomeView로 셋팅 
                navBar.setTitle('LoginView');
                btnHomeView.show (); //화면이 HomeView이면 Home 버튼은 숨김                
                btnLoginViewRight.hide();
                btnLoginViewLeft.hide();
                btnListView.show();
            }
            
        });
        
        var btnLoginViewLeft = Ext.create('Ext.Button', {
            text:'Login',
            ui:'back',
            align:'left',
            hidden:true,
            handler:function(btn,event){
                //화면 전환 transition 화면 전환 효과
                rootPanel.getLayout().setAnimation({
                    type:'slide',
                    direction:'right',
                    
                });
                
                // 화면 지정
                rootPanel.setActiveItem(loginView);
                
                //Titlebar의 제목을 LoginView로 셋팅 
                navBar.setTitle('LoginView');
                btnHomeView.show(); //화면이 HomeView이면 Home 버튼은 숨김                
                btnLoginViewRight.hide();
                btnLoginViewLeft.hide();
                btnListView.show();
            }
            
        });
        
        var btnListView = Ext.create('Ext.Button', {
            text:'Login',
            ui:'back',
            align:'left',
            hidden:true,
            handler:function(btn,event){
                //화면 전환 transition 화면 전환 효과
                rootPanel.getLayout().setAnimation({
                    type:'slide',
                    direction:'right',
                    
                });
                
                // 화면 지정
                rootPanel.setActiveItem(loginView);
                
                //Titlebar의 제목을 LoginView로 셋팅 
                navBar.setTitle('LoginView');
                btnHomeView.show(); //화면이 HomeView이면 Home 버튼은 숨김                
                btnLoginViewRight.hide();
                btnLoginViewLeft.hide();
                btnListView.show();
            }
            
        });
        
        var btnListView = Ext.create('Ext.Button', {
            text:'List',
            ui:'forward',
            align:'right',
            hidden:true,
            handler:function(btn,event){
                //화면 전환 transition 화면 전환 효과
                rootPanel.getLayout().setAnimation({
                    type:'slide',
                    direction:'left',
                    
                });
                
                // 화면 지정
                rootPanel.setActiveItem(listView);
                
                //Titlebar의 제목을 listView로 셋팅 
                navBar.setTitle('ListView');
                btnHomeView.hide(); //화면이 HomeView이면 Home 버튼은 숨김                
                btnLoginViewRight.hide();
                btnLoginViewLeft.show();
                btnListView.hide();
            }
            
        });
        
        
        var navBar = Ext.create('Ext.TitleBar', {
            docked:'top',
            ui:'light',
            title:'HomeView',
            items:[btnHomeView, btnLoginViewRight, btnLoginViewLeft, btnListView]
        });
        
        var rootPanel = Ext.create('Ext.Panel', {
            
            fullscreen:true,
            layout:'card',
            items:[navBar, homeView]
                 
        });
        
 
    }
});
 
 
cs


Java SDK 다운로드 

http://www.oracle.com/technetwork/java/javase/downloads/index.html



개발툴: Spring Tool Suite

https://spring.io/tools/sts

(나중에 jetbrains Intellij 사용)



한글을 이용하기 위한 이클립스 설정 설정 

Window - Preferences - General - Content Types



Java Properties File 항목을 클릭하고 마찬가지로 Default encoding을 UTF-8로 바꾸고 Update 버튼 클릭



Web-> JSP Files 에서도 Encoding을 UTF-8로 바꾼다

Maven 설치 및 설정 

http://maven.apache.org/download.cgi


자기 OS 버전에 맞는 파일을 다운로드 하여 압축을 풀고 패스 경로를 지정해준다




Apache 톰캣서버 - 기존방법대로 설치

톰캣 압축 파일 OS버전에 맞춰 톰캣 7.0 다운로드 

Eclipse Preferences 에서 runtime environment 에서 tomcat 7.0 설정

File -> New -> Other -> Server 로 가서 Tomcat 서버 추가


Tomcat 서버 클릭후 Tomcat 서버 압축 푼곳에 경로 설정


서버추가후 Servers 폴더가 나타나는데 

server.xml 안에 두부분 아래와 같이 수정 (한글처리를 위해)

1
<Connector connectionTimeout="20000" port="9999" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
cs
1
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"  URIEncoding="UTF-8"/>
cs


메이븐을 활용한 프로젝트 생성

메이븐 기본개념 정리 블로그 - 

http://dimdim.tistory.com/entry/Maven-%EC%A0%95%EB%A6%AC

메이븐 설치후 프로젝트가 들어갈 워크스페이스 폴더에 위치 시킨다

폴도 윈도우 안에 마우스를 두고 오른쪽을 클릭하면 여기에 명령창 열기가 컨텍스트 메뉴에 추가되는데 클릭한후 위에 명령어를 실행한다. 


첫번째 프로픔트가 깜빡거릴때 엔터키를 누르고

Choose a number :: 6 :: 다음에 깜빡거릴때 엔터기를 누른다 이때 받아들인 버전은 archetype 1.1 버전을 말한다. 

세번째 프롬프트가 'groupId' :: 다음에 깜빡거릴때 com.ensoa



Eclipse 로 Maven 프로젝트 만들기


요소

 설명

<groupId>

프로젝트 그룹ID 

<artifactId> 

프로젝트 아티팩트 ID 

<version>

버전 

<packaging> 

패키징 .방식 : jar, war, ear 

<dependencies> 

이 프로젝트가 의존하는 다른 프로젝트 정보 

메이븐 프로젝트 의존성 설정

프로젝트가 의존하는 다른 프로젝트에 대한 정보는 <dependancy> 요소로 설장한다 . <dependency> 요소 안에는 다음 서브 요소를 포함한다. 


 요소

설명 

<groupId> 

의존 프로젝트 그룹 ID 

<artifactId> 

의존 프로젝트 아티팩트 ID 

<version> 

버전 

Ex) 프로젝트에서 스프링 프레임 워크를 사용하고자 한다면, Spring 프레임 워크 의존성을 추가해야 한다. 

1
2
3
4
5
6
7
<dependencies>
    <dependency>
     <groupdId>org.springframework</groupdId>
     <artifactId>spring-context</artifactId>
     <version>4.1.1.RELEASE</version>
    </dependency>
</dependencies>
cs

스프링 프레임 워크를 사용하기 위해서는 spring-context 모듈만 필요한것은 아님

스프링 프레임워크는 19개의 독립적인 모듈 즉, jar 파일로 구성되어 있다. 

실제 Maven으로 다운로드한 pom.xml을 보면 많은 프로젝트가 의존하고 있는것을 볼수 있다


의존성을 설정할 때 <scope> 요소를 사용하여 의존성 범위를 지정 가능 


 설정 값

설명 

compile 

컴파일 시에 필요함. 실행과 테스트 시에도 포함됨. 디폴트 값

runtime 

실행시 필요함. 컴파일에는 필요하지 않지만, 실행 할 때 필요하며 배포 할때 포함됨 

provided 

컴파일할 때는 필요하지만, 실행 시에는 컨테이너 등에서 기본으로 제공하기 때문에 배포할 필요가 없음.  

test 

테스트 코드를 컴파일 할때 필요함. 테스트 시에는 포함되지만, 배포 시에는 제외됨 


레파지토리 



HTML 내장 DB - sqlite3


01.html


소스코드 참고

openDatabase = 데이터베이스 검사한후 데이터베이스가 존재하면 생성하지 않음 

Init작업을 했는데 왜 또 Init을 하는지



크로스 도메인 처리

서버가 다르면 보안규칙에 위배되어 AJAX 통신이 되지 않는다 

AJAX는 기보적으로 다른 서버와의 통신이 안된다

다른서버와의 통신은 JSONP로만 데이터로만 가능하다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript">
//기존 JSON방식
/*     $(function(){
        $.ajax({
            type:'GET',
            url:'jqm_nojsonp.jsp',
            dataType:'json',
            success:function(data){
                $('#item_list').empty();
                $.each(data, function(index, item){
                    var str='';
                    str += '<li>';
                    str += '<a href="#">';
                    str += '<h3>' + item.code +' :' + item.name + '</h3>';
                    str += '<p> 수량' + item.quantity + ', 가격 ' + item.price + '</p>';
                    str += '</a>';
                    str += '</li>';
                    
                    $('#item_list').append(str);
                    
                });
                
                //리스트뷰 작업이 완료되었으면 리스트뷰를 refresh
                $('#item_list').listview('refresh');
            }        
        });
    }); */
 
    /* 
        ajax 사용시 크로스 도메인의 경우 JSONP을 이용해야 함.
        보안을 위해 소스 근원지가 같을 때만 데이터 통신을 허용하기 때문에 
        데이터를 호출하는 도메인과 데이터를 반환하는 도메인이 일치해야 함
        JSONP(JSON with PAdding) : 보안 정책에 부합하면서 악성코드를 
        막고 오픈 API들의 서비스도 사용할수 있도록 JSONP 형시그을 사용해서 
        데이터를 주고 받음 
        
    */
    //JSONP형식
    $(function(){
        $.ajax({
            type:'GET',
            url:'http://dragonzone.cafe24.com/jqm_jsonp.jsp',
            dataType:'jsonp',
            //Jquery 버전에 따라 jsoncCallback 으로 인식할 수 있음
            /* jsonp:'dragonp', */
            jsonpCallback: 'dragonp',
            success:function(data){
                $('#item_list').empty();
                $.each(data, function(index, item){
                    var str='';
                    str += '<li>';
                    str += '<a href="#">';
                    str += '<h3>' + item.code +' :' + item.name + '</h3>';
                    str += '<p> 수량' + item.quantity + ', 가격 ' + item.price + '</p>';
                    str += '</a>';
                    str += '</li>';
                    
                    $('#item_list').append(str);
                    
                });
                
                //리스트뷰 작업이 완료되었으면 리스트뷰를 refresh
                $('#item_list').listview('refresh');
            }        
        });
    });
</script>
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" id="first_page">
        <!-- header  -->
        <div data-role="header" data-position="fixed">
            <h1>JSONP 처리</h1>
            <!-- 두번째 페이지로 넘어가지만 창이 하나 뜬 느낌을 주고 싶을떄 -->            
        </div>
        
        <!-- 내용  -->        
        <div role="main" class="ui-content">
            <ul data-role="listview" id="item_list" data-inset="true"
                
            </ul>
        </div>    
 
        
        <!-- footer  -->
        <div data-role="footer" data-position="fixed">
            <h1>화면 하단</h1>
        </div>    
    </div>
 
</body>
 
</html>
cs



센차 터치

JavaScript로 HTML도 처리하는 방식

sencha-touch-all 모든 라이브러리

sencha-touch - 필요할떄마다 라이브러리 부르는 형식


대부분의 예제에서 쓰일 기본 HTML 레이아웃 코드 

app.js 부분만 파일명에 따라 교체해주면 된다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>SenchaTouch</title>
 
<link rel="stylesheet" type="text/css" href="../touch/resources/css/sencha-touch.css">
 
<script type="text/javascript" src="../touch/sencha-touch-all-debug.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body>
    
</body>
</html>
cs

센차 터치 기본 코드 양식

1
2
3
4
5
6
7
8
9
10
11
12
13
Ext.application({
    name:'Myapp',
    launch:function(){
 
        var rootPanel = Ext.create('Ext.Panel', {
            //Ext.viewport.add(panel) 대신 아래 fullscreen:true 로 대체
            fullscreen:true,
            items:[panel]
                 
        });
 
    }
});
cs


app.js

1
2
3
4
5
6
7
8
9
10
11
//브라우저가 렌더링하여 HTML 생성
//초기화 코드
Ext.application({
    //웹앱의 이름을 지정
    name:'MyApp',
    launch:function(){
        alert('launch-run');        
    }
});
 
 
cs

app2.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
Ext.application({
    name:'MyApp',
    launch:function(){
        //패널 생성                이름        옵션
        var panel = Ext.create('Ext.Panel', {
            html:'<br><br><div align="center">Hello World</div>',
            style:'background-color:#ffff00'
        });
        //뷰포트는 기본적으로 비어있어서 컴포넌트를 추가하면 화면을 꽉 채움 
        Ext.Viewport.add(panel);
    }
});
 
 
cs

app3.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 
Ext.application({
    name:'Myapp',
    launch:function(){
        var top = Ext.create('Ext.Panel', {
            //위치 지정
            docked: 'top',
            style: 'background-color:blue; font-size:40px',
            html:'TOP'
            
        });        
        var bottom = Ext.create('Ext.Panel', {
            docked:'bottom',
            style:'background-color:green;font-size:40px',
            html:'<font color="yellow">BOTTOM</font>'
        });        
        var panel = Ext.create('Ext.Panel', {
            //Ext.viewport.add(panel) 대신 아래 fullscreen:true 로 대체
            fullscreen:true,
            items:[top, bottom],
            html:'Panel 바탕입니다. <br>이곳에 글자가 쓰여집니다.' +
                 '<br><font color="red">이제 시작해 볼까요?</font>'+
                 '<br><br><br><div align="center"><img src="./image/user.png" width="60" height="60"></div>'
        });
        //
        //Ext.Viewport.add(panel);
    }
});
 
 
cs

app4.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Ext.application({
    name:'Myapp',
    launch:function(){
        var dockedItem1 = Ext.create('Ext.Panel', {
            //위치 지정
            docked: 'top',
            style: 'background-color:red;',
            html:'top'
            
        });
        
        var dockedItem2 = Ext.create('Ext.Panel', {
            //위치 지정
            docked: 'left',
            style: 'background-color:gray;',
            html:'left'
                
        });
        
        var dockedItem3 = Ext.create('Ext.Panel', {
            //위치 지정
            docked: 'bottom',
            style: 'background-color:yellow;',
            html:'bottom'
            
        });    
        
        var dockedItem4 = Ext.create('Ext.Panel', {
            //위치 지정
            docked: 'right',
            style: 'background-color:green;',
            html:'right'
            
        });    
 
        var rootPanel = Ext.create('Ext.Panel', {
            //Ext.viewport.add(panel) 대신 아래 fullscreen:true 로 대체
            fullscreen:true,
            items:[dockedItem1, dockedItem2, dockedItem3, dockedItem4],
            html:'내용이 보여짐'
                 
        });
        //
        //Ext.Viewport.add(panel);
    }
});
 
 
cs

app5.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
Ext.application({
    name:'Myapp',
    launch:function(){
        
        var panel = Ext.create('Ext.Panel',{
            style:'background-color:#00ff00',
            //html 속성으로 지정하기에 복잡한 내용은 html 문서의 body 부분에 
            //지정하고 id를 부여해 호출 가능
            //HTML구조가 복잡하기때문에 아래와 같은 경우 많음
            contentEl:'panelContent'
        });
        
 
        var rootPanel = Ext.create('Ext.Panel', {
            //Ext.viewport.add(panel) 대신 아래 fullscreen:true 로 대체
            fullscreen:true,
            items:[panel]
                 
        });
        //
        //Ext.Viewport.add(panel);
    }
});
 
 
cs



화면전환 방법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" id="first_page">
        <!-- header  -->
        <div data-role="header">
            <h1>화면전환 효과</h1>            
        </div>
        
        <!-- 내용  -->
        
        <div role="main" class="ui-content">
        <div data-role="listview">
            <li><a href="#second_page" data-transition="slide" class="ui-btn">slide</a>
            <li><a href="#second_page" data-transition="slideup" class="ui-btn">slideup</a>
            <li><a href="#second_page" data-transition="slidedown" class="ui-btn">slidedown</a>
            <li><a href="#second_page" data-transition="pop" class="ui-btn">pop</a>
            <li><a href="#second_page" data-transition="fade" class="ui-btn">fade(기본)</a>
            <li><a href="#second_page" data-transition="flip" class="ui-btn">flip</a>
        </div>
        </div>
        
        <!-- footer  -->
        <div data-role="footer" data-theme="b">
            <h1>화면 하단</h1>
        </div>    
    </div>
    
    <!-- 두번째 페이지 -->
    <div data-role="page" id="second_page">
        <!-- header  -->
        <div data-role="header">
            <h1>두번째 페이지</h1>
            <a href="#" data-rel="back" class="ui-btn ui-corner-all ui-icon-back ui-btn-icon-left ui-btn-icon-notext">이전</a>            
        </div>
        
        <!-- 내용  -->    
        <div role="main" class="ui-content">
            <p> 두번째 페이지</p>
        </div>        
        <!-- footer  -->
        <div data-role="footer" data-theme="b">
            <h1>화면 하단</h1>
        </div>
    </div>
</body>
</html>
cs


다이얼로그 창 (기본값 팝업)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" id="first_page">
        <!-- header  -->
        <div data-role="header">
            <h1>Dialog</h1>
            <!-- 두번째 페이지로 넘어가지만 창이 하나 뜬 느낌을 주고 싶을떄 -->            
        </div>
        
        <!-- 내용  -->
        
        <div role="main" class="ui-content">
        <div data-role="listview">
            <li><a href="#second_page" data-rel="dialog">slide</a>
            <li><a href="#second_page" data-rel="dialog" data-transition="slide" >slide</a>
            <li><a href="#second_page" data-rel="dialog" data-transition="slideup" class="ui-btn">slideup</a>
            <li><a href="#second_page" data-rel="dialog" data-transition="slidedown" class="ui-btn">slidedown</a>
            <li><a href="#second_page" data-rel="dialog" data-transition="pop" class="ui-btn">pop(기본)</a>
            <li><a href="#second_page" data-rel="dialog" data-transition="fade" class="ui-btn">fade</a>
            <li><a href="#second_page" data-rel="dialog" data-transition="flip" class="ui-btn">flip</a>
        </div>
        </div>        
        <!-- footer  -->
        <div data-role="footer" data-theme="b">
            <h1>화면 하단</h1>
        </div>    
    </div>
    
    <!-- 두번째 페이지 -->
    <div data-role="page" id="second_page">
        <!-- header  -->
        <div data-role="header">
            <h1>두번째 페이지</h1>
            <a href="#" data-rel="back" class="ui-btn ui-corner-all ui-icon-back ui-btn-icon-left ui-btn-icon-notext">이전</a>            
        </div>
        
        <!-- 내용  -->    
        <div role="main" class="ui-content">
            <p> 두번째 페이지</p>
        </div>
        
 
        
        <!-- footer  -->
        <div data-role="footer" data-theme="b">
            <h1>화면 하단</h1>
        </div>
    </div>
</body>
</html>
cs

펼치기 메뉴

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" >
        <!-- header  -->
        <div data-role="header">
            <h1>Collapsible</h1>
                        
        </div>
        
        <!-- 내용  -->        
        <div role="main" class="ui-content">
        
            <h4>기본 Collapsible</h4>
            <div data-role="collapsible">
                <h4>우리 마을의 역사는?</h4>
                <p>우리 마을 은 고려시대  왕이 사냥하던 지역이었다.</p>    
            </div>
            
            <h4>테마</h4>
            <div data-role="collapsible" data-theme="b" data-content-theme="b">
                <h4>올해 겨울 날씨는?</h4>
                <p>겨울에 눈이 많이 오고 삼한 시온 현상이 강할 것으로 예상</p>    
            </div>
            
            <h4>확장된 Collapsible</h4>
            <!-- data-collpased="false" 를 기재하면 로딩시 펼쳐진 형태로 보여짐 -->
            <div data-role="collapsible" data-callpased="false">
                <h4>수강과목</h4>
                <ul data-role="listview">
                    <li><a href="#">List 1</a></li>
                    <li><a href="#">List 2</a></li>
                    <li><a href="#">List 3</a></li>
                </ul>    
            </div>
            
            <h4>아이콘 사용</h4>
            <!-- 아이콘 선택 가능 -->
            <div data-role="collapsible" data-collapsed-icon="carat-d" data-expanded-icon="carat-u">
                <h4>수강과목</h4>
                <ul data-role="listview">
                    <li><a href="#">List 1</a></li>
                    <li><a href="#">List 2</a></li>
                    <li><a href="#">List 3</a></li>
                </ul>    
            </div>
            
            <h4>모서리가 둥글지 않은 Collapsible</h4>
            <div data-role="collapsible" data-inset="false">
                <h4>수강과목</h4>
                <ul data-role="listview">
                    <li><a href="#">List 1</a></li>
                    <li><a href="#">List 2</a></li>
                    <li><a href="#">List 3</a></li>
                </ul>    
            </div>
                        
 
        </div>
        
        <!-- footer  -->
        <div data-role="footer" data-theme="b">
            <h1>화면 하단</h1>
        </div>    
    </div>
</body>
</html>
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
    <!-- 첫번째 페이지 -->
    <div data-role="page">
        <!-- header -->
        <div data-role="header">
            <h1>Collapsible</h1>
        </div>
        <!-- 내용 -->
        <div role="main" class="ui-content">
            <h4>Collapsible Set</h4>
            <div data-role="collapsibleset">
                <div data-role="collapsible">
                    <h3>애완동물</h3>
                    <ul data-role="listview">
                      <li><a href="#">개</a></li>
                      <li><a href="#">고양이</a></li>
                      <li><a href="#">뱀</a></li>
                    </ul>
                </div>
                <div data-role="collapsible">
                    <h3>스포츠</h3>
                    <ul data-role="listview">
                      <li><a href="#">농구</a></li>
                      <li><a href="#">배구</a></li>
                      <li><a href="#">축구</a></li>
                    </ul>
                </div>
            </div>
        </div>
        <!-- footer -->
        <div data-role="footer">
            <h1>화면 하단</h1>
        </div>
    </div>
</body>
</html>
cs

Grid

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" >
        <!-- header  -->
        <div data-role="header">
            <h1>grid</h1>
                        
        </div>
        
        <!-- 내용  -->        
        <div role="main" class="ui-content">
<!--  
그리드 정의     CSS 컬럼수         컬럼에 포함되는 항목 CSS
ui-grid-a        2개                ui-block-a, ui-block-b
ui-grid-b        3개             ui-block-a, ui-block-b, ui-block-c
ui-grid-c        4개             ui-block-a, ui-block-b, ui-block-c, ui-block-d
ui-grid-d        5개                ui-block-a, ui-block-b, ui-block-c, ui-block-d, ui-block-e
-->
            <h4>2개로 분할</h4>
            <div class="ui-grid-a">
                <div class="ui-block-a"><div class="ui-bar ui-bar-a" style="height:60px">Block A</div></div>                        
                <div class="ui-block-b"><div class="ui-bar ui-bar-a" style="height:60px">Block B</div></div>
            </div>
                
            <h4>폼에서 사용할 수 있는 좌우(2개) 분할</h4>
            <div class="ui-grid-a">
                <div class="ui-block-a"><input type="submit" value="전송"></div>                    
                <div class="ui-block-b"><input type="reset" value="리셋"></div>
            </div>
            
            <h4>3개 분할</h4>
            <div class="ui-grid-b">
                <div class="ui-block-a"><div class="ui-bar ui-bar-a" style="height:60px">Block A</div></div>                        
                <div class="ui-block-b"><div class="ui-bar ui-bar-a" style="height:60px">Block B</div></div>
                <div class="ui-block-c"><div class="ui-bar ui-bar-a" style="height:60px">Block C</div></div>
            </div>    
                    
            <h4>4개 분할</h4>
            <div class="ui-grid-c">
                <div class="ui-block-a"><div class="ui-bar ui-bar-a" style="height:60px">Block A</div></div>                        
                <div class="ui-block-b"><div class="ui-bar ui-bar-a" style="height:60px">Block B</div></div>
                <div class="ui-block-c"><div class="ui-bar ui-bar-a" style="height:60px">Block C</div></div>
                <div class="ui-block-d"><div class="ui-bar ui-bar-a" style="height:60px">Block D</div></div>
            </div>
            
            <h4>5개 분할</h4>
            <div class="ui-grid-d">
                <div class="ui-block-a"><div class="ui-bar ui-bar-a" style="height:60px">Block A</div></div>                        
                <div class="ui-block-b"><div class="ui-bar ui-bar-a" style="height:60px">Block B</div></div>
                <div class="ui-block-c"><div class="ui-bar ui-bar-a" style="height:60px">Block C</div></div>
                <div class="ui-block-d"><div class="ui-bar ui-bar-a" style="height:60px">Block D</div></div>
                <div class="ui-block-e"><div class="ui-bar ui-bar-a" style="height:60px">Block E</div></div>
            </div>    
            
        </div>
        
        <!-- footer  -->
        <div data-role="footer">
            <h1>화면 하단</h1>
        </div>    
    </div>
</body>
</html>
cs

테이블

모바일 환경에서는 테이블을 잘쓰지 않는다(데이터 출력 문제)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" >
        <!-- header  -->
        <div data-role="header">
            <h1>grid</h1>
                        
        </div
        <!-- end of header  -->
        
        <!-- 내용  -->        
        <div role="main" class="ui-content">
            <h4>기본 컬럼토글</h4>
            <table data-role="table" id="table-column-toggle" 
                data-mode="columntoggle" class="ui-responsive table-stroke">
                
                <thead>
                    <tr>
                    <!-- 
                        data-priority를 명시해야 토글 메뉴에 표시
                        컬럼이 보여지는 우선순 1이 가장 높고 6이 가장 낮음
                     -->
                        <th data-priority="2">Rank</th>
                        <th>Movie TItle</th>
                        <th data-priority="3">Year</th>
                        <th data-priority="1">Rating</th>
                        <th data-priority="5">Reviews</th>                    
                    </tr>
                
                </thead>
                <tbody>    
                    <tr>
                        <th>1</th>
                        <td>Citizen Kane</td>
                        <td>1941</td>
                        <td>100%</td>
                        <td>74</td>
                    </tr>
                    <tr>
                        <th>2</th>
                        <td>올드보이</td>
                        <td>2000</td>
                        <td>100%</td>
                        <td>74</td>
                    </tr>
                    <tr>
                        <th>3</th>
                        <td>설국열차</td>
                        <td>2013</td>
                        <td>98%</td>
                        <td>72</td>
                    </tr>
                </tbody>                
                
                
            </table>                    
        </div
        <!-- end of main  -->
        
        <!-- footer  -->
        <div data-role="footer">
            <h1>화면 하단</h1>
        </div>
        <!-- end of footer  -->    
    </div>
</body>
</html>
cs

네이게이션 바

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" >
        <!-- header  -->
        <div data-role="header">
            <h1>navbar</h1>
            <div data-role="navbar">
                <ul>
                    <li><a href="#">One</a></li>
                    <li><a href="#">Two</a></li>
                    <li><a href="#">Three</a></li>
                    <li><a href="#">Four</a></li>
                    <li><a href="#">Five</a></li>
                </ul>
            </div>    
        </div
        <!-- end of header  -->
        
        <!-- 내용  -->        
        <div role="main" class="ui-content">
            <!-- data-iconpos 아이콘 위치 지정 -->
            <h4>아이콘 사용</h4>
            <div data-role="navbar" data-iconpos="right">
                <ul>
                    <li><a href="" data-icon="grid">Summary</a></li>
                    <li><a href="" data-icon="star">List</a></li>
                    <li><a href="" data-icon="gear    ">Write</a></li>
                    
                </ul>
            </div>    
            
            <h4>화면 로드시 항목 선택</h4>
            <div data-role="navbar" data-iconpos="right">
                <ul>
                    <li><a href="#" class="ui-btn-active" >One</a></li>
                    <li><a href="#">Two</a></li>
                    <li><a href="#">Three</a></li>
                    <li><a href="#">Four</a></li>
                    <li><a href="#">Five</a></li>                
                </ul>
            </div>                    
        </div
        <!-- end of main  -->
        
        <!-- footer  -->
        <div data-role="footer">
            <h4>화면 하단</h4>
            <div data-role="navbar" data-iconpos="right">
                <ul>
                    <li><a href="#" class="ui-btn-active" >One</a></li>
                    <li><a href="#">Two</a></li>
                    <li><a href="#">Three</a></li>
                    <li><a href="#">Four</a></li>
                    <li><a href="#">Five</a></li>                
                </ul>
            </div>        
        </div>
        <!-- end of footer  -->    
    </div>
</body>
</html>
 
cs

각각 다른파일로 연결

02_a.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" >
        <!-- header  -->
        <div data-role="header">
            <h1>첫번째 페이지</h1>
        </div
        <!-- end of header  -->
        
        <!-- 내용  -->        
        <div role="main" class="ui-content">
            <p>첫번째 페이지</p>                
        </div
        <!-- end of main  -->
        
        <!-- footer  -->
        <div data-role="footer">
            <h4>화면 하단</h4>
            <!-- data-iconpos를 지정하지 않으면 기본값은 top -->
            <div data-role="navbar" data-iconpos="right">
                <ul>
                    <li><a href="02_a.html" data-icon="grid" 
                    class="btn-active ui-state-persist">One</a></li>
                    <li><a href="02_b.html" data-icon="star">Two</a></li>
                    <li><a href="02_c.html" data-icon="gear">Three</a></li>            
                </ul>
            </div>
    
        </div>
        <!-- end of footer  -->    
    </div>
</body>
</html>
 
cs

02_b.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" >
        <!-- header  -->
        <div data-role="header">
            <h1>두번째 페이지</h1>
        </div
        <!-- end of header  -->
        
        <!-- 내용  -->        
        <div role="main" class="ui-content">
            <p>두번째 페이지</p>            
        </div
        <!-- end of main  -->
        
        <!-- footer  -->
        <div data-role="footer">
            <h4>화면 하단</h4>
            <div data-role="navbar" data-iconpos="right">
                <ul>
                    <li><a href="02_a.html" data-icon="grid" 
                    >One</a></li>
                    <li><a href="02_b.html" data-icon="star"
                    class="btn-active ui-state-persist">Two</a></li>
                    <li><a href="02_c.html" data-icon="gear">Three</a></li>            
                </ul>
            </div>
    
        </div>
        <!-- end of footer  -->    
    </div>
</body>
</html>
 
cs

02_c.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" >
        <!-- header  -->
        <div data-role="header">
            <h1>세번째 페이지</h1>
        </div
        <!-- end of header  -->
        
        <!-- 내용  -->        
        <div role="main" class="ui-content">
            <p>세번째 페이지</p>                
        </div
        <!-- end of main  -->
        
        <!-- footer  -->
        <div data-role="footer">
            <h4>화면 하단</h4>
            <div data-role="navbar" data-iconpos="right">
                <ul>
                    <li><a href="02_a.html" data-icon="grid" 
                    >One</a></li>
                    <li><a href="02_b.html" data-icon="star">Two</a></li>
                    <li><a href="02_c.html" data-icon="gear"
                    class="btn-active ui-state-persist">Three</a></li>            
                </ul>
            </div>
    
        </div>
        <!-- end of footer  -->    
    </div>
</body>
</html>
 
cs

제어쿼리 머릿말 과 내용 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
 
<html>
<head>
<meta charset="UTF-8">
<title>jQuery Mobile</title>
 
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="../mobile/jquery.mobile-1.4.5.min.css">
<script type="text/javascript" src="../mobile/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../mobile/jquery.mobile-1.4.5.min.js"></script>
 
<body>
    <!-- jQueryMobile 에서 페이지 만드는 첫번째 방법  -->
    
    <!-- 첫번째 페이지 -->
    <div data-role="page" >
        <!-- header  -->
        <div data-role="header">
            <h1>table</h1>
                        
        </div
        <!-- end of header  -->
        
        <!-- 내용  -->        
        <div role="main" class="ui-content">
            <h4>머릿말과 내용</h4>
            <h3 class="ui-bar ui-bar-a">jQuery Mobile</h3>
            <div>
                <div class="ui-body">
                    <p>모바일 환경에 최적화된 페이지를 만들수 있습니다.
                    </p>
                </div>
                
            </div>
            
            <h4>모서리가 둥근 머릿글과 내용</h4>
            <h3 class="ui-bar ui-bar-a ui-corner-all">jQuery Mobile</h3>
            <div>
                <div class="ui-body">
                    <pre>모바일 환경에 최적화된 페이지를 만들수 있습니다.
                    냐하하하하하
                    </pre>
                </div>
                
            </div>        
            
            <h4>모서리가 둥근 머릿글과 모서리가 둥근 내용</h4>
            <h3 class="ui-bar ui-bar-a ui-corner-all">jQuery Mobile</h3>
            <div>
                <div class="ui-body ui-body-a ui-corner-all">
                    <p>모바일 환경에 최적화된 페이지를 만들수 있습니다.    </p>
                </div>                
            </div>    
            
            <h4>모서리가 둥근 머릿글과 내용이 분리되지 않은 형태</h4>
            <h3 class="ui-bar ui-bar-a ui-corner-all">jQuery Mobile</h3>
            <p>모바일 환경에 최적화된 페이지를 만들수 있습니다.</p>                
            
        <!--<h4>모서리가 둥글며 머릿글과 내용이 붙어 있는 형태</h4>
            <h3 class="ui-bar ui-bar-a ui-corner-all">jQuery Mobile</h3>
            
                <div class="ui-body ui-body-a ui-corner-all">
                    <p>모바일 환경에 최적화된 페이지를 만들수 있습니다.    </p>
                </div>     -->                    
                
        </div
        <!-- end of main  -->
        
        <!-- footer  -->
        <div data-role="footer">
            <h1>화면 하단</h1>
        </div>
        <!-- end of footer  -->    
    </div>
</body>
</html>
cs


+ Recent posts